From 8d1e6170a0c7ebd0089d932cdf0fb480a820d8c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 01:13:15 +0000 Subject: [PATCH] chore: update all dependencies --- .github/workflows/commitlint.yml | 2 +- .github/workflows/delete-untagged-images.yml | 2 +- .github/workflows/go.yml | 2 +- .github/workflows/lint-pr.yml | 2 +- .github/workflows/zizmor.yml | 4 +- go.mod | 2 +- go.sum | 2 + mcp/go.mod | 6 +- mcp/go.sum | 6 + .../google/jsonschema-go/jsonschema/infer.go | 5 +- .../jsonschema-go/jsonschema/validate.go | 11 +- .../go-sdk/auth/authorization_code.go | 41 +++ .../go-sdk/internal/jsonrpc2/conn.go | 121 ++----- .../go-sdk/internal/jsonrpc2/frame.go | 208 ----------- .../go-sdk/internal/jsonrpc2/jsonrpc2.go | 70 +--- .../go-sdk/internal/jsonrpc2/messages.go | 12 - .../go-sdk/internal/jsonrpc2/net.go | 138 -------- .../go-sdk/internal/jsonrpc2/serve.go | 330 ------------------ .../go-sdk/internal/jsonrpc2/wire.go | 3 - .../go-sdk/internal/util/errors.go | 16 + .../go-sdk/internal/util/util.go | 44 --- .../modelcontextprotocol/go-sdk/mcp/client.go | 7 +- .../go-sdk/mcp/protocol.go | 22 +- .../modelcontextprotocol/go-sdk/mcp/server.go | 19 +- .../modelcontextprotocol/go-sdk/mcp/shared.go | 18 +- .../modelcontextprotocol/go-sdk/mcp/sse.go | 39 ++- .../go-sdk/mcp/streamable.go | 106 ++++-- .../go-sdk/mcp/streamable_headers.go | 98 ++++++ .../go-sdk/mcp/transport.go | 24 +- .../go-sdk/oauthex/dcr.go | 7 +- .../golang.org/x/sys/cpu/cpu_other_arm64.go | 2 +- .../golang.org/x/sys/cpu/cpu_windows.go | 26 ++ .../golang.org/x/sys/cpu/cpu_windows_arm64.go | 38 ++ .../golang.org/x/sys/cpu/zcpu_windows.go | 48 +++ mcp/vendor/modules.txt | 6 +- .../golang.org/x/sys/unix/affinity_linux.go | 128 ++++++- vendor/golang.org/x/sys/unix/mkall.sh | 2 +- vendor/golang.org/x/sys/unix/syscall_linux.go | 8 +- .../x/sys/unix/syscall_linux_arm.go | 3 + .../x/sys/unix/syscall_linux_arm64.go | 3 + .../x/sys/unix/syscall_linux_loong64.go | 3 + .../x/sys/unix/syscall_linux_riscv64.go | 3 + .../golang.org/x/sys/unix/zsyscall_linux.go | 4 +- .../x/sys/windows/syscall_windows.go | 13 +- .../golang.org/x/sys/windows/types_windows.go | 29 ++ .../x/sys/windows/zsyscall_windows.go | 36 ++ vendor/modules.txt | 2 +- 47 files changed, 720 insertions(+), 1001 deletions(-) delete mode 100644 mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/frame.go delete mode 100644 mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/net.go delete mode 100644 mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/serve.go create mode 100644 mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/util/errors.go delete mode 100644 mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/util/util.go create mode 100644 mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/streamable_headers.go create mode 100644 mcp/vendor/golang.org/x/sys/cpu/cpu_windows.go create mode 100644 mcp/vendor/golang.org/x/sys/cpu/cpu_windows_arm64.go create mode 100644 mcp/vendor/golang.org/x/sys/cpu/zcpu_windows.go diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 092935421..be3aca935 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 + uses: step-security/harden-runner@a5ad31d6a139d249332a2605b85202e8c0b78450 # v2.19.1 with: egress-policy: audit diff --git a/.github/workflows/delete-untagged-images.yml b/.github/workflows/delete-untagged-images.yml index 50e49bcda..a4dfcc1fc 100644 --- a/.github/workflows/delete-untagged-images.yml +++ b/.github/workflows/delete-untagged-images.yml @@ -15,7 +15,7 @@ jobs: environment: scheduled steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 + uses: step-security/harden-runner@a5ad31d6a139d249332a2605b85202e8c0b78450 # v2.19.1 with: egress-policy: audit diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index a4767a494..cd9077821 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 + uses: step-security/harden-runner@a5ad31d6a139d249332a2605b85202e8c0b78450 # v2.19.1 with: egress-policy: audit diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml index b7278956a..8e558e806 100644 --- a/.github/workflows/lint-pr.yml +++ b/.github/workflows/lint-pr.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 + uses: step-security/harden-runner@a5ad31d6a139d249332a2605b85202e8c0b78450 # v2.19.1 with: egress-policy: audit diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 152148a41..957bcac57 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -17,7 +17,7 @@ jobs: actions: read steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 + uses: step-security/harden-runner@a5ad31d6a139d249332a2605b85202e8c0b78450 # v2.19.1 with: egress-policy: audit @@ -35,7 +35,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 with: sarif_file: results.sarif category: zizmor \ No newline at end of file diff --git a/go.mod b/go.mod index a462f2a92..46f8fa844 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/rivo/uniseg v0.4.7 github.com/spf13/cobra v1.10.2 github.com/zekroTJA/timedmap/v2 v2.0.0 - golang.org/x/sys v0.43.0 + golang.org/x/sys v0.44.0 golang.org/x/term v0.42.0 golang.org/x/text v0.36.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 diff --git a/go.sum b/go.sum index a3b656b53..122ba8f8a 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/zekroTJA/timedmap/v2 v2.0.0/go.mod h1:xHDLg687zASqLBJqoysF+WORHxL/kYN go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= diff --git a/mcp/go.mod b/mcp/go.mod index d71f6ede4..ad9de222e 100644 --- a/mcp/go.mod +++ b/mcp/go.mod @@ -6,18 +6,18 @@ replace github.com/netapp/harvest/v2 => ../ require ( github.com/goccy/go-yaml v1.19.2 - github.com/modelcontextprotocol/go-sdk v1.5.0 + github.com/modelcontextprotocol/go-sdk v1.6.0 github.com/netapp/harvest/v2 v2.0.0-20260327161100-5f32698d9c23 github.com/spf13/cobra v1.10.2 ) require ( - github.com/google/jsonschema-go v0.4.2 // indirect + github.com/google/jsonschema-go v0.4.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/segmentio/asm v1.2.1 // indirect github.com/segmentio/encoding v0.5.4 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sys v0.43.0 // indirect + golang.org/x/sys v0.44.0 // indirect ) diff --git a/mcp/go.sum b/mcp/go.sum index c02c504df..2873c0f7e 100644 --- a/mcp/go.sum +++ b/mcp/go.sum @@ -7,6 +7,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= +github.com/google/jsonschema-go v0.4.3 h1:/DBOLZTfDow7pe2GmaJNhltueGTtDKICi8V8p+DQPd0= +github.com/google/jsonschema-go v0.4.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/modelcontextprotocol/go-sdk v1.4.0 h1:u0kr8lbJc1oBcawK7Df+/ajNMpIDFE41OEPxdeTLOn8= @@ -15,6 +17,8 @@ github.com/modelcontextprotocol/go-sdk v1.4.1 h1:M4x9GyIPj+HoIlHNGpK2hq5o3BFhC+7 github.com/modelcontextprotocol/go-sdk v1.4.1/go.mod h1:Bo/mS87hPQqHSRkMv4dQq1XCu6zv4INdXnFZabkNU6s= github.com/modelcontextprotocol/go-sdk v1.5.0 h1:CHU0FIX9kpueNkxuYtfYQn1Z0slhFzBZuq+x6IiblIU= github.com/modelcontextprotocol/go-sdk v1.5.0/go.mod h1:gggDIhoemhWs3BGkGwd1umzEXCEMMvAnhTrnbXJKKKA= +github.com/modelcontextprotocol/go-sdk v1.6.0 h1:PPLS3kn7WtOEnR+Af4X5H96SG0qSab8R/ZQT/HkhPkY= +github.com/modelcontextprotocol/go-sdk v1.6.0/go.mod h1:kzm3kzFL1/+AziGOE0nUs3gvPoNxMCvkxokMkuFapXQ= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= @@ -36,6 +40,8 @@ golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/mcp/vendor/github.com/google/jsonschema-go/jsonschema/infer.go b/mcp/vendor/github.com/google/jsonschema-go/jsonschema/infer.go index 9c195f579..1b3d975df 100644 --- a/mcp/vendor/github.com/google/jsonschema-go/jsonschema/infer.go +++ b/mcp/vendor/github.com/google/jsonschema-go/jsonschema/infer.go @@ -7,6 +7,7 @@ package jsonschema import ( + "encoding" "fmt" "log/slog" "maps" @@ -199,14 +200,12 @@ func forType(t reflect.Type, seen map[reflect.Type]bool, ignore bool, schemas ma // Unrestricted case reflect.Map: - if t.Key().Kind() != reflect.String { + if t.Key().Kind() != reflect.String && !t.Key().Implements(reflect.TypeFor[encoding.TextMarshaler]()) { if ignore { return nil, nil // ignore } return nil, fmt.Errorf("unsupported map key type %v", t.Key().Kind()) } - if t.Key().Kind() != reflect.String { - } s.Type = "object" s.AdditionalProperties, err = forType(t.Elem(), seen, ignore, schemas) if err != nil { diff --git a/mcp/vendor/github.com/google/jsonschema-go/jsonschema/validate.go b/mcp/vendor/github.com/google/jsonschema-go/jsonschema/validate.go index bbef5906c..03fe40936 100644 --- a/mcp/vendor/github.com/google/jsonschema-go/jsonschema/validate.go +++ b/mcp/vendor/github.com/google/jsonschema-go/jsonschema/validate.go @@ -262,14 +262,15 @@ func (st *state) validate(instance reflect.Value, schema *Schema, callerAnns *an } if schema.AnyOf != nil { // We must visit them all, to collect annotations. - ok := false + var errs []error for _, ss := range schema.AnyOf { - if valid(ss, &anns) { - ok = true + if err := st.validate(instance, ss, &anns); err != nil { + errs = append(errs, err) } } - if !ok { - return fmt.Errorf("anyOf: did not validate against any of %v", schema.AnyOf) + if len(errs) == len(schema.AnyOf) { + return fmt.Errorf("anyOf: did not validate against any of %v:\n%v", + schema.AnyOf, errors.Join(errs...)) } } if schema.OneOf != nil { diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/auth/authorization_code.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/auth/authorization_code.go index 846feb7b9..758b88e3a 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/auth/authorization_code.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/auth/authorization_code.go @@ -15,6 +15,7 @@ import ( "slices" "strings" + "github.com/modelcontextprotocol/go-sdk/internal/util" "github.com/modelcontextprotocol/go-sdk/oauthex" "golang.org/x/oauth2" ) @@ -34,6 +35,10 @@ type ClientIDMetadataDocumentConfig struct { type DynamicClientRegistrationConfig struct { // Metadata to be used in dynamic client registration request as per // https://datatracker.ietf.org/doc/html/rfc7591#section-2. + // + // If Metadata.ApplicationType is empty, it will be inferred from + // Metadata.RedirectURIs. When set, it will be validated against the inferred type + // and an error will be returned if they conflict. Metadata *oauthex.ClientRegistrationMetadata } @@ -150,6 +155,12 @@ func NewAuthorizationCodeHandler(config *AuthorizationCodeHandlerConfig) (*Autho } else if !slices.Contains(dCfg.Metadata.RedirectURIs, config.RedirectURL) { return nil, fmt.Errorf("RedirectURL %q is not in the list of allowed redirect URIs for dynamic client registration", config.RedirectURL) } + applicationType := inferApplicationType(dCfg.Metadata.RedirectURIs) + if dCfg.Metadata.ApplicationType == "" { + dCfg.Metadata.ApplicationType = applicationType + } else if dCfg.Metadata.ApplicationType != applicationType { + return nil, fmt.Errorf("application type %q conflicts with the application type inferred from redirect URIs", dCfg.Metadata.ApplicationType) + } } if config.RedirectURL == "" { // If the RedirectURL was supposed to be set by the dynamic client registration, @@ -170,6 +181,36 @@ func isNonRootHTTPSURL(u string) bool { return pu.Scheme == "https" && pu.Path != "" } +// inferApplicationType returns an application type based on the redirect URIs. +func inferApplicationType(redirectURIs []string) string { + hasNative := false + hasWeb := false + for _, uri := range redirectURIs { + u, err := url.Parse(uri) + if err != nil { + return "" + } + switch u.Scheme { + case "http", "https": + if util.IsLoopback(u.Hostname()) { + hasNative = true + } else { + hasWeb = true + } + default: + hasNative = true + } + } + + if hasNative && hasWeb { + return "" + } + if hasNative { + return "native" + } + return "web" +} + // Authorize performs the authorization flow. // It is designed to perform the whole Authorization Code Grant flow. // On success, [AuthorizationCodeHandler.TokenSource] will return a token source with the fetched token. diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/conn.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/conn.go index 571df63aa..72cc74083 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/conn.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/conn.go @@ -16,45 +16,6 @@ import ( "github.com/modelcontextprotocol/go-sdk/internal/json" ) -// Binder builds a connection configuration. -// This may be used in servers to generate a new configuration per connection. -// ConnectionOptions itself implements Binder returning itself unmodified, to -// allow for the simple cases where no per connection information is needed. -type Binder interface { - // Bind returns the ConnectionOptions to use when establishing the passed-in - // Connection. - // - // The connection is not ready to use when Bind is called, - // but Bind may close it without reading or writing to it. - Bind(context.Context, *Connection) ConnectionOptions -} - -// A BinderFunc implements the Binder interface for a standalone Bind function. -type BinderFunc func(context.Context, *Connection) ConnectionOptions - -func (f BinderFunc) Bind(ctx context.Context, c *Connection) ConnectionOptions { - return f(ctx, c) -} - -var _ Binder = BinderFunc(nil) - -// ConnectionOptions holds the options for new connections. -type ConnectionOptions struct { - // Framer allows control over the message framing and encoding. - // If nil, HeaderFramer will be used. - Framer Framer - // Preempter allows registration of a pre-queue message handler. - // If nil, no messages will be preempted. - Preempter Preempter - // Handler is used as the queued message handler for inbound messages. - // If nil, all responses will be ErrNotHandled. - Handler Handler - // OnInternalError, if non-nil, is called with any internal errors that occur - // while serving the connection, such as protocol errors or invariant - // violations. (If nil, internal errors result in panics.) - OnInternalError func(error) -} - // Connection manages the jsonrpc2 protocol, connecting responses back to their // calls. Connection is bidirectional; it does not have a designated server or // client end. @@ -197,9 +158,28 @@ type incomingRequest struct { cancel context.CancelFunc } -// Bind returns the options unmodified. -func (o ConnectionOptions) Bind(context.Context, *Connection) ConnectionOptions { - return o +// Reader abstracts the transport mechanics from the JSON RPC protocol. +// A Connection reads messages from the reader it was provided on construction, +// and assumes that each call to Read fully transfers a single message, +// or returns an error. +// +// A reader is not safe for concurrent use, it is expected it will be used by +// a single Connection in a safe manner. +type Reader interface { + // Read gets the next message from the stream. + Read(context.Context) (Message, error) +} + +// Writer abstracts the transport mechanics from the JSON RPC protocol. +// A Connection writes messages using the writer it was provided on construction, +// and assumes that each call to Write fully transfers a single message, +// or returns an error. +// +// A writer must be safe for concurrent use, as writes may occur concurrently +// in practice: libraries may make calls or respond to requests asynchronously. +type Writer interface { + // Write sends a message to the stream. + Write(context.Context, Message) error } // A ConnectionConfig configures a bidirectional jsonrpc2 connection. @@ -230,59 +210,16 @@ func NewConnection(ctx context.Context, cfg ConnectionConfig) *Connection { return c } -// bindConnection creates a new connection and runs it. -// -// This is used by the Dial and Serve functions to build the actual connection. -// -// The connection is closed automatically (and its resources cleaned up) when -// the last request has completed after the underlying ReadWriteCloser breaks, -// but it may be stopped earlier by calling Close (for a clean shutdown). -func bindConnection(bindCtx context.Context, rwc io.ReadWriteCloser, binder Binder, onDone func()) *Connection { - // TODO: Should we create a new event span here? - // This will propagate cancellation from ctx; should it? - ctx := notDone{bindCtx} - - c := &Connection{ - state: inFlightState{closer: rwc}, - done: make(chan struct{}), - onDone: onDone, - } - // It's tempting to set a finalizer on c to verify that the state has gone - // idle when the connection becomes unreachable. Unfortunately, the Binder - // interface makes that unsafe: it allows the Handler to close over the - // Connection, which could create a reference cycle that would cause the - // Connection to become uncollectable. - - options := binder.Bind(bindCtx, c) - framer := options.Framer - if framer == nil { - framer = HeaderFramer() - } - c.handler = options.Handler - if c.handler == nil { - c.handler = defaultHandler{} - } - c.onInternalError = options.OnInternalError - - c.writer = framer.Writer(rwc) - reader := framer.Reader(rwc) - c.start(ctx, reader, options.Preempter) - return c -} - func (c *Connection) start(ctx context.Context, reader Reader, preempter Preempter) { c.updateInFlight(func(s *inFlightState) { select { case <-c.done: - // Bind already closed the connection; don't start a goroutine to read it. + // The connection was already closed; don't start a goroutine to read it. return default: } // The goroutine started here will continue until the underlying stream is closed. - // - // (If the Binder closed the Connection already, this should error out and - // return almost immediately.) s.reading = true go c.readIncoming(ctx, reader, preempter) }) @@ -439,18 +376,6 @@ type AsyncCall struct { // This can be used to cancel the call if needed. func (ac *AsyncCall) ID() ID { return ac.id } -// IsReady can be used to check if the result is already prepared. -// This is guaranteed to return true on a result for which Await has already -// returned, or a call that failed to send in the first place. -func (ac *AsyncCall) IsReady() bool { - select { - case <-ac.ready: - return true - default: - return false - } -} - // retire processes the response to the call. // // It is an error to call retire more than once: retire is guarded by the diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/frame.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/frame.go deleted file mode 100644 index 72527cb9c..000000000 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/frame.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2018 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 jsonrpc2 - -import ( - "bufio" - "context" - "encoding/json" - "fmt" - "io" - "strconv" - "strings" - "sync" -) - -// Reader abstracts the transport mechanics from the JSON RPC protocol. -// A Conn reads messages from the reader it was provided on construction, -// and assumes that each call to Read fully transfers a single message, -// or returns an error. -// -// A reader is not safe for concurrent use, it is expected it will be used by -// a single Conn in a safe manner. -type Reader interface { - // Read gets the next message from the stream. - Read(context.Context) (Message, error) -} - -// Writer abstracts the transport mechanics from the JSON RPC protocol. -// A Conn writes messages using the writer it was provided on construction, -// and assumes that each call to Write fully transfers a single message, -// or returns an error. -// -// A writer must be safe for concurrent use, as writes may occur concurrently -// in practice: libraries may make calls or respond to requests asynchronously. -type Writer interface { - // Write sends a message to the stream. - Write(context.Context, Message) error -} - -// Framer wraps low level byte readers and writers into jsonrpc2 message -// readers and writers. -// It is responsible for the framing and encoding of messages into wire form. -// -// TODO(rfindley): rethink the framer interface, as with JSONRPC2 batching -// there is a need for Reader and Writer to be correlated, and while the -// implementation of framing here allows that, it is not made explicit by the -// interface. -// -// Perhaps a better interface would be -// -// Frame(io.ReadWriteCloser) (Reader, Writer). -type Framer interface { - // Reader wraps a byte reader into a message reader. - Reader(io.Reader) Reader - // Writer wraps a byte writer into a message writer. - Writer(io.Writer) Writer -} - -// RawFramer returns a new Framer. -// The messages are sent with no wrapping, and rely on json decode consistency -// to determine message boundaries. -func RawFramer() Framer { return rawFramer{} } - -type rawFramer struct{} -type rawReader struct{ in *json.Decoder } -type rawWriter struct { - mu sync.Mutex - out io.Writer -} - -func (rawFramer) Reader(rw io.Reader) Reader { - return &rawReader{in: json.NewDecoder(rw)} -} - -func (rawFramer) Writer(rw io.Writer) Writer { - return &rawWriter{out: rw} -} - -func (r *rawReader) Read(ctx context.Context) (Message, error) { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - var raw json.RawMessage - if err := r.in.Decode(&raw); err != nil { - return nil, err - } - msg, err := DecodeMessage(raw) - return msg, err -} - -func (w *rawWriter) Write(ctx context.Context, msg Message) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - - data, err := EncodeMessage(msg) - if err != nil { - return fmt.Errorf("marshaling message: %v", err) - } - - w.mu.Lock() - defer w.mu.Unlock() - _, err = w.out.Write(data) - return err -} - -// HeaderFramer returns a new Framer. -// The messages are sent with HTTP content length and MIME type headers. -// This is the format used by LSP and others. -func HeaderFramer() Framer { return headerFramer{} } - -type headerFramer struct{} -type headerReader struct{ in *bufio.Reader } -type headerWriter struct { - mu sync.Mutex - out io.Writer -} - -func (headerFramer) Reader(rw io.Reader) Reader { - return &headerReader{in: bufio.NewReader(rw)} -} - -func (headerFramer) Writer(rw io.Writer) Writer { - return &headerWriter{out: rw} -} - -func (r *headerReader) Read(ctx context.Context) (Message, error) { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - - firstRead := true // to detect a clean EOF below - var contentLength int64 - // read the header, stop on the first empty line - for { - line, err := r.in.ReadString('\n') - if err != nil { - if err == io.EOF { - if firstRead && line == "" { - return nil, io.EOF // clean EOF - } - err = io.ErrUnexpectedEOF - } - return nil, fmt.Errorf("failed reading header line: %w", err) - } - firstRead = false - - line = strings.TrimSpace(line) - // check we have a header line - if line == "" { - break - } - colon := strings.IndexRune(line, ':') - if colon < 0 { - return nil, fmt.Errorf("invalid header line %q", line) - } - name, value := line[:colon], strings.TrimSpace(line[colon+1:]) - switch { - case strings.EqualFold(name, "Content-Length"): - if contentLength, err = strconv.ParseInt(value, 10, 32); err != nil { - return nil, fmt.Errorf("failed parsing Content-Length: %v", value) - } - if contentLength <= 0 { - return nil, fmt.Errorf("invalid Content-Length: %v", contentLength) - } - default: - // ignoring unknown headers - } - } - if contentLength == 0 { - return nil, fmt.Errorf("missing Content-Length header") - } - data := make([]byte, contentLength) - _, err := io.ReadFull(r.in, data) - if err != nil { - return nil, err - } - msg, err := DecodeMessage(data) - return msg, err -} - -func (w *headerWriter) Write(ctx context.Context, msg Message) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - w.mu.Lock() - defer w.mu.Unlock() - - data, err := EncodeMessage(msg) - if err != nil { - return fmt.Errorf("marshaling message: %v", err) - } - _, err = fmt.Fprintf(w.out, "Content-Length: %v\r\n\r\n", len(data)) - if err == nil { - _, err = w.out.Write(data) - } - return err -} diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/jsonrpc2.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/jsonrpc2.go index 234e6ee3a..76a511613 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/jsonrpc2.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/jsonrpc2.go @@ -12,17 +12,12 @@ import ( "errors" ) -var ( - // ErrIdleTimeout is returned when serving timed out waiting for new connections. - ErrIdleTimeout = errors.New("timed out waiting for new connections") - - // ErrNotHandled is returned from a Handler or Preempter to indicate it did - // not handle the request. - // - // If a Handler returns ErrNotHandled, the server replies with - // ErrMethodNotFound. - ErrNotHandled = errors.New("JSON RPC not handled") -) +// ErrNotHandled is returned from a Handler or Preempter to indicate it did +// not handle the request. +// +// If a Handler returns ErrNotHandled, the server replies with +// ErrMethodNotFound. +var ErrNotHandled = errors.New("JSON RPC not handled") // Preempter handles messages on a connection before they are queued to the main // handler. @@ -40,15 +35,6 @@ type Preempter interface { Preempt(ctx context.Context, req *Request) (result any, err error) } -// A PreempterFunc implements the Preempter interface for a standalone Preempt function. -type PreempterFunc func(ctx context.Context, req *Request) (any, error) - -func (f PreempterFunc) Preempt(ctx context.Context, req *Request) (any, error) { - return f(ctx, req) -} - -var _ Preempter = PreempterFunc(nil) - // Handler handles messages on a connection. type Handler interface { // Handle is invoked sequentially for each incoming request that has not @@ -67,16 +53,6 @@ type Handler interface { Handle(ctx context.Context, req *Request) (result any, err error) } -type defaultHandler struct{} - -func (defaultHandler) Preempt(context.Context, *Request) (any, error) { - return nil, ErrNotHandled -} - -func (defaultHandler) Handle(context.Context, *Request) (any, error) { - return nil, ErrNotHandled -} - // A HandlerFunc implements the Handler interface for a standalone Handle function. type HandlerFunc func(ctx context.Context, req *Request) (any, error) @@ -85,37 +61,3 @@ func (f HandlerFunc) Handle(ctx context.Context, req *Request) (any, error) { } var _ Handler = HandlerFunc(nil) - -// async is a small helper for operations with an asynchronous result that you -// can wait for. -type async struct { - ready chan struct{} // closed when done - firstErr chan error // 1-buffered; contains either nil or the first non-nil error -} - -func newAsync() *async { - var a async - a.ready = make(chan struct{}) - a.firstErr = make(chan error, 1) - a.firstErr <- nil - return &a -} - -func (a *async) done() { - close(a.ready) -} - -func (a *async) wait() error { - <-a.ready - err := <-a.firstErr - a.firstErr <- err - return err -} - -func (a *async) setError(err error) { - storedErr := <-a.firstErr - if storedErr == nil { - storedErr = err - } - a.firstErr <- storedErr -} diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/messages.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/messages.go index b424780eb..77c953136 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/messages.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/messages.go @@ -11,8 +11,6 @@ import ( "fmt" internaljson "github.com/modelcontextprotocol/go-sdk/internal/json" - - "github.com/modelcontextprotocol/go-sdk/internal/mcpgodebug" ) // ID is a Request identifier, which is defined by the spec to be a string, integer, or null. @@ -219,18 +217,8 @@ func marshalToRaw(obj any) (json.RawMessage, error) { return json.RawMessage(data), nil } -// jsonescaping is a compatibility parameter that allows to restore -// JSON escaping in the JSON marshaling, which stopped being the default -// in the 1.4.0 version of the SDK. See the documentation for the -// mcpgodebug package for instructions how to enable it. -// The option will be removed in the 1.6.0 version of the SDK. -var jsonescaping = mcpgodebug.Value("jsonescaping") - // jsonMarshal marshals obj to JSON like json.Marshal but without HTML escaping. func jsonMarshal(obj any) ([]byte, error) { - if jsonescaping == "1" { - return json.Marshal(obj) - } var buf bytes.Buffer enc := json.NewEncoder(&buf) enc.SetEscapeHTML(false) diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/net.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/net.go deleted file mode 100644 index 05db06261..000000000 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/net.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2018 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 jsonrpc2 - -import ( - "context" - "io" - "net" - "os" -) - -// This file contains implementations of the transport primitives that use the standard network -// package. - -// NetListenOptions is the optional arguments to the NetListen function. -type NetListenOptions struct { - NetListenConfig net.ListenConfig - NetDialer net.Dialer -} - -// NetListener returns a new Listener that listens on a socket using the net package. -func NetListener(ctx context.Context, network, address string, options NetListenOptions) (Listener, error) { - ln, err := options.NetListenConfig.Listen(ctx, network, address) - if err != nil { - return nil, err - } - return &netListener{net: ln}, nil -} - -// netListener is the implementation of Listener for connections made using the net package. -type netListener struct { - net net.Listener -} - -// Accept blocks waiting for an incoming connection to the listener. -func (l *netListener) Accept(context.Context) (io.ReadWriteCloser, error) { - return l.net.Accept() -} - -// Close will cause the listener to stop listening. It will not close any connections that have -// already been accepted. -func (l *netListener) Close() error { - addr := l.net.Addr() - err := l.net.Close() - if addr.Network() == "unix" { - rerr := os.Remove(addr.String()) - if rerr != nil && err == nil { - err = rerr - } - } - return err -} - -// Dialer returns a dialer that can be used to connect to the listener. -func (l *netListener) Dialer() Dialer { - return NetDialer(l.net.Addr().Network(), l.net.Addr().String(), net.Dialer{}) -} - -// NetDialer returns a Dialer using the supplied standard network dialer. -func NetDialer(network, address string, nd net.Dialer) Dialer { - return &netDialer{ - network: network, - address: address, - dialer: nd, - } -} - -type netDialer struct { - network string - address string - dialer net.Dialer -} - -func (n *netDialer) Dial(ctx context.Context) (io.ReadWriteCloser, error) { - return n.dialer.DialContext(ctx, n.network, n.address) -} - -// NetPipeListener returns a new Listener that listens using net.Pipe. -// It is only possibly to connect to it using the Dialer returned by the -// Dialer method, each call to that method will generate a new pipe the other -// side of which will be returned from the Accept call. -func NetPipeListener(ctx context.Context) (Listener, error) { - return &netPiper{ - done: make(chan struct{}), - dialed: make(chan io.ReadWriteCloser), - }, nil -} - -// netPiper is the implementation of Listener build on top of net.Pipes. -type netPiper struct { - done chan struct{} - dialed chan io.ReadWriteCloser -} - -// Accept blocks waiting for an incoming connection to the listener. -func (l *netPiper) Accept(context.Context) (io.ReadWriteCloser, error) { - // Block until the pipe is dialed or the listener is closed, - // preferring the latter if already closed at the start of Accept. - select { - case <-l.done: - return nil, net.ErrClosed - default: - } - select { - case rwc := <-l.dialed: - return rwc, nil - case <-l.done: - return nil, net.ErrClosed - } -} - -// Close will cause the listener to stop listening. It will not close any connections that have -// already been accepted. -func (l *netPiper) Close() error { - // unblock any accept calls that are pending - close(l.done) - return nil -} - -func (l *netPiper) Dialer() Dialer { - return l -} - -func (l *netPiper) Dial(ctx context.Context) (io.ReadWriteCloser, error) { - client, server := net.Pipe() - - select { - case l.dialed <- server: - return client, nil - - case <-l.done: - client.Close() - server.Close() - return nil, net.ErrClosed - } -} diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/serve.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/serve.go deleted file mode 100644 index 424163aaf..000000000 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/serve.go +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright 2020 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 jsonrpc2 - -import ( - "context" - "fmt" - "io" - "runtime" - "sync" - "sync/atomic" - "time" -) - -// Listener is implemented by protocols to accept new inbound connections. -type Listener interface { - // Accept accepts an inbound connection to a server. - // It blocks until either an inbound connection is made, or the listener is closed. - Accept(context.Context) (io.ReadWriteCloser, error) - - // Close closes the listener. - // Any blocked Accept or Dial operations will unblock and return errors. - Close() error - - // Dialer returns a dialer that can be used to connect to this listener - // locally. - // If a listener does not implement this it will return nil. - Dialer() Dialer -} - -// Dialer is used by clients to dial a server. -type Dialer interface { - // Dial returns a new communication byte stream to a listening server. - Dial(ctx context.Context) (io.ReadWriteCloser, error) -} - -// Server is a running server that is accepting incoming connections. -type Server struct { - listener Listener - binder Binder - async *async - - shutdownOnce sync.Once - closing int32 // atomic: set to nonzero when Shutdown is called -} - -// Dial uses the dialer to make a new connection, wraps the returned -// reader and writer using the framer to make a stream, and then builds -// a connection on top of that stream using the binder. -// -// The returned Connection will operate independently using the Preempter and/or -// Handler provided by the Binder, and will release its own resources when the -// connection is broken, but the caller may Close it earlier to stop accepting -// (or sending) new requests. -// -// If non-nil, the onDone function is called when the connection is closed. -func Dial(ctx context.Context, dialer Dialer, binder Binder, onDone func()) (*Connection, error) { - // dial a server - rwc, err := dialer.Dial(ctx) - if err != nil { - return nil, err - } - return bindConnection(ctx, rwc, binder, onDone), nil -} - -// NewServer starts a new server listening for incoming connections and returns -// it. -// This returns a fully running and connected server, it does not block on -// the listener. -// You can call Wait to block on the server, or Shutdown to get the sever to -// terminate gracefully. -// To notice incoming connections, use an intercepting Binder. -func NewServer(ctx context.Context, listener Listener, binder Binder) *Server { - server := &Server{ - listener: listener, - binder: binder, - async: newAsync(), - } - go server.run(ctx) - return server -} - -// Wait returns only when the server has shut down. -func (s *Server) Wait() error { - return s.async.wait() -} - -// Shutdown informs the server to stop accepting new connections. -func (s *Server) Shutdown() { - s.shutdownOnce.Do(func() { - atomic.StoreInt32(&s.closing, 1) - s.listener.Close() - }) -} - -// run accepts incoming connections from the listener, -// If IdleTimeout is non-zero, run exits after there are no clients for this -// duration, otherwise it exits only on error. -func (s *Server) run(ctx context.Context) { - defer s.async.done() - - var activeConns sync.WaitGroup - for { - rwc, err := s.listener.Accept(ctx) - if err != nil { - // Only Shutdown closes the listener. If we get an error after Shutdown is - // called, assume that was the cause and don't report the error; - // otherwise, report the error in case it is unexpected. - if atomic.LoadInt32(&s.closing) == 0 { - s.async.setError(err) - } - // We are done generating new connections for good. - break - } - - // A new inbound connection. - activeConns.Add(1) - _ = bindConnection(ctx, rwc, s.binder, activeConns.Done) // unregisters itself when done - } - activeConns.Wait() -} - -// NewIdleListener wraps a listener with an idle timeout. -// -// When there are no active connections for at least the timeout duration, -// calls to Accept will fail with ErrIdleTimeout. -// -// A connection is considered inactive as soon as its Close method is called. -func NewIdleListener(timeout time.Duration, wrap Listener) Listener { - l := &idleListener{ - wrapped: wrap, - timeout: timeout, - active: make(chan int, 1), - timedOut: make(chan struct{}), - idleTimer: make(chan *time.Timer, 1), - } - l.idleTimer <- time.AfterFunc(l.timeout, l.timerExpired) - return l -} - -type idleListener struct { - wrapped Listener - timeout time.Duration - - // Only one of these channels is receivable at any given time. - active chan int // count of active connections; closed when Close is called if not timed out - timedOut chan struct{} // closed when the idle timer expires - idleTimer chan *time.Timer // holds the timer only when idle -} - -// Accept accepts an incoming connection. -// -// If an incoming connection is accepted concurrent to the listener being closed -// due to idleness, the new connection is immediately closed. -func (l *idleListener) Accept(ctx context.Context) (io.ReadWriteCloser, error) { - rwc, err := l.wrapped.Accept(ctx) - - select { - case n, ok := <-l.active: - if err != nil { - if ok { - l.active <- n - } - return nil, err - } - if ok { - l.active <- n + 1 - } else { - // l.wrapped.Close Close has been called, but Accept returned a - // connection. This race can occur with concurrent Accept and Close calls - // with any net.Listener, and it is benign: since the listener was closed - // explicitly, it can't have also timed out. - } - return l.newConn(rwc), nil - - case <-l.timedOut: - if err == nil { - // Keeping the connection open would leave the listener simultaneously - // active and closed due to idleness, which would be contradictory and - // confusing. Close the connection and pretend that it never happened. - rwc.Close() - } else { - // In theory the timeout could have raced with an unrelated error return - // from Accept. However, ErrIdleTimeout is arguably still valid (since we - // would have closed due to the timeout independent of the error), and the - // harm from returning a spurious ErrIdleTimeout is negligible anyway. - } - return nil, ErrIdleTimeout - - case timer := <-l.idleTimer: - if err != nil { - // The idle timer doesn't run until it receives itself from the idleTimer - // channel, so it can't have called l.wrapped.Close yet and thus err can't - // be ErrIdleTimeout. Leave the idle timer as it was and return whatever - // error we got. - l.idleTimer <- timer - return nil, err - } - - if !timer.Stop() { - // Failed to stop the timer — the timer goroutine is in the process of - // firing. Send the timer back to the timer goroutine so that it can - // safely close the timedOut channel, and then wait for the listener to - // actually be closed before we return ErrIdleTimeout. - l.idleTimer <- timer - rwc.Close() - <-l.timedOut - return nil, ErrIdleTimeout - } - - l.active <- 1 - return l.newConn(rwc), nil - } -} - -func (l *idleListener) Close() error { - select { - case _, ok := <-l.active: - if ok { - close(l.active) - } - - case <-l.timedOut: - // Already closed by the timer; take care not to double-close if the caller - // only explicitly invokes this Close method once, since the io.Closer - // interface explicitly leaves doubled Close calls undefined. - return ErrIdleTimeout - - case timer := <-l.idleTimer: - if !timer.Stop() { - // Couldn't stop the timer. It shouldn't take long to run, so just wait - // (so that the Listener is guaranteed to be closed before we return) - // and pretend that this call happened afterward. - // That way we won't leak any timers or goroutines when Close returns. - l.idleTimer <- timer - <-l.timedOut - return ErrIdleTimeout - } - close(l.active) - } - - return l.wrapped.Close() -} - -func (l *idleListener) Dialer() Dialer { - return l.wrapped.Dialer() -} - -func (l *idleListener) timerExpired() { - select { - case n, ok := <-l.active: - if ok { - panic(fmt.Sprintf("jsonrpc2: idleListener idle timer fired with %d connections still active", n)) - } else { - panic("jsonrpc2: Close finished with idle timer still running") - } - - case <-l.timedOut: - panic("jsonrpc2: idleListener idle timer fired more than once") - - case <-l.idleTimer: - // The timer for this very call! - } - - // Close the Listener with all channels still blocked to ensure that this call - // to l.wrapped.Close doesn't race with the one in l.Close. - defer close(l.timedOut) - l.wrapped.Close() -} - -func (l *idleListener) connClosed() { - select { - case n, ok := <-l.active: - if !ok { - // l is already closed, so it can't close due to idleness, - // and we don't need to track the number of active connections any more. - return - } - n-- - if n == 0 { - l.idleTimer <- time.AfterFunc(l.timeout, l.timerExpired) - } else { - l.active <- n - } - - case <-l.timedOut: - panic("jsonrpc2: idleListener idle timer fired before last active connection was closed") - - case <-l.idleTimer: - panic("jsonrpc2: idleListener idle timer active before last active connection was closed") - } -} - -type idleListenerConn struct { - wrapped io.ReadWriteCloser - l *idleListener - closeOnce sync.Once -} - -func (l *idleListener) newConn(rwc io.ReadWriteCloser) *idleListenerConn { - c := &idleListenerConn{ - wrapped: rwc, - l: l, - } - - // A caller that forgets to call Close may disrupt the idleListener's - // accounting, even though the file descriptor for the underlying connection - // may eventually be garbage-collected anyway. - // - // Set a (best-effort) finalizer to verify that a Close call always occurs. - // (We will clear the finalizer explicitly in Close.) - runtime.SetFinalizer(c, func(c *idleListenerConn) { - panic("jsonrpc2: IdleListener connection became unreachable without a call to Close") - }) - - return c -} - -func (c *idleListenerConn) Read(p []byte) (int, error) { return c.wrapped.Read(p) } -func (c *idleListenerConn) Write(p []byte) (int, error) { return c.wrapped.Write(p) } - -func (c *idleListenerConn) Close() error { - defer c.closeOnce.Do(func() { - c.l.connClosed() - runtime.SetFinalizer(c, nil) - }) - return c.wrapped.Close() -} diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/wire.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/wire.go index c0a41bffb..4d123f2c0 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/wire.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2/wire.go @@ -28,9 +28,6 @@ var ( // The following errors are not part of the json specification, but // compliant extensions specific to this implementation. - // ErrServerOverloaded is returned when a message was refused due to a - // server being temporarily unable to accept any new messages. - ErrServerOverloaded = NewError(-32000, "overloaded") // ErrUnknown should be used for all non coded errors. ErrUnknown = NewError(-32001, "unknown error") // ErrServerClosing is returned for calls that arrive while the server is closing. diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/util/errors.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/util/errors.go new file mode 100644 index 000000000..ebeb6149a --- /dev/null +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/util/errors.go @@ -0,0 +1,16 @@ +// 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 util + +import ( + "fmt" +) + +// Wrapf wraps *errp with the given formatted message if *errp is not nil. +func Wrapf(errp *error, format string, args ...any) { + if *errp != nil { + *errp = fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), *errp) + } +} diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/util/util.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/util/util.go deleted file mode 100644 index 4b5c325fa..000000000 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/internal/util/util.go +++ /dev/null @@ -1,44 +0,0 @@ -// 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 util - -import ( - "cmp" - "fmt" - "iter" - "slices" -) - -// Helpers below are copied from gopls' moremaps package. - -// Sorted returns an iterator over the entries of m in key order. -func Sorted[M ~map[K]V, K cmp.Ordered, V any](m M) iter.Seq2[K, V] { - // TODO(adonovan): use maps.Sorted if proposal #68598 is accepted. - return func(yield func(K, V) bool) { - keys := KeySlice(m) - slices.Sort(keys) - for _, k := range keys { - if !yield(k, m[k]) { - break - } - } - } -} - -// KeySlice returns the keys of the map M, like slices.Collect(maps.Keys(m)). -func KeySlice[M ~map[K]V, K comparable, V any](m M) []K { - r := make([]K, 0, len(m)) - for k := range m { - r = append(r, k) - } - return r -} - -// Wrapf wraps *errp with the given formatted message if *errp is not nil. -func Wrapf(errp *error, format string, args ...any) { - if *errp != nil { - *errp = fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), *errp) - } -} diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/client.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/client.go index dc7bef1c5..c82e189d9 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/client.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/client.go @@ -253,7 +253,7 @@ func (c *Client) capabilities(protocolVersion string) *ClientCapabilities { // server, calls or notifications will return an error wrapping // [ErrConnectionClosed]. func (c *Client) Connect(ctx context.Context, t Transport, opts *ClientSessionOptions) (cs *ClientSession, err error) { - cs, err = connect(ctx, t, c, (*clientSessionState)(nil), nil) + cs, err = connect(ctx, t, c, (*clientSessionState)(nil), nil, c.opts.Logger) if err != nil { return nil, err } @@ -340,7 +340,8 @@ func (cs *ClientSession) ID() string { // Close is idempotent and concurrency safe. func (cs *ClientSession) Close() error { // Note: keepaliveCancel access is safe without a mutex because: - // 1. keepaliveCancel is only written once during startKeepalive (happens-before all Close calls) + // 1. keepaliveCancel is only written once during Client.Connect (through startKeepalive), + // which happens before any code that may call Close from another goroutine // 2. context.CancelFunc is safe to call multiple times and from multiple goroutines // 3. The keepalive goroutine calls Close on ping failure, but this is safe since // Close is idempotent and conn.Close() handles concurrent calls correctly @@ -404,7 +405,7 @@ func (cs *ClientSession) registerElicitationWaiter(elicitationID string) (await // startKeepalive starts the keepalive mechanism for this client session. func (cs *ClientSession) startKeepalive(interval time.Duration) { - startKeepalive(cs, interval, &cs.keepaliveCancel) + startKeepalive(cs, interval, &cs.keepaliveCancel, cs.client.opts.Logger) } // AddRoots adds the given roots to the client, diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/protocol.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/protocol.go index 0b6bf4704..1646788ac 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/protocol.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/protocol.go @@ -10,6 +10,7 @@ import ( "maps" internaljson "github.com/modelcontextprotocol/go-sdk/internal/json" + "github.com/modelcontextprotocol/go-sdk/internal/mcpgodebug" ) // Optional annotations for the client. The client can use annotations to inform @@ -113,10 +114,25 @@ type CallToolResult struct { err error } -// SetError sets the error for the tool result and populates the Content field -// with the error text. It also sets IsError to true. +// seterroroverwrite is a compatibility parameter that restores the pre-1.6.0 +// behavior of [CallToolResult.SetError], where Content was always overwritten +// with the error text. See the documentation for the mcpgodebug package for +// instructions on how to enable it. +// The option will be removed in the 1.8.0 version of the SDK. +var seterroroverwrite = mcpgodebug.Value("seterroroverwrite") + +// SetError sets the error for the tool result and sets IsError to true. +// If Content has not already been populated, it is set to the error text. +// If Content has already been populated, it is left unchanged, allowing callers +// to provide a user-friendly message while still recording the underlying error +// for inspection via [GetError] in server middleware. +// +// To restore the previous behavior where Content was always overwritten, +// set MCPGODEBUG=seterroroverwrite=1. func (r *CallToolResult) SetError(err error) { - r.Content = []Content{&TextContent{Text: err.Error()}} + if len(r.Content) == 0 || seterroroverwrite == "1" { + r.Content = []Content{&TextContent{Text: err.Error()}} + } r.IsError = true r.err = err } diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/server.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/server.go index 97b5d4462..285043763 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/server.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/server.go @@ -1026,11 +1026,18 @@ func (s *Server) Connect(ctx context.Context, t Transport, opts *ServerSessionOp } s.opts.Logger.Info("server connecting") - ss, err := connect(ctx, t, s, state, onClose) + ss, err := connect(ctx, t, s, state, onClose, s.opts.Logger) if err != nil { s.opts.Logger.Error("server connect error", "error", err) return nil, err } + + // Start keepalive before returning the session to avoid race conditions with Close. + // This is safe because the spec allows sending pings before initialization (see ServerSession.handle for details). + if s.opts.KeepAlive > 0 { + ss.startKeepalive(ss.server.opts.KeepAlive) + } + return ss, nil } @@ -1058,9 +1065,6 @@ func (ss *ServerSession) initialized(ctx context.Context, params *InitializedPar ss.server.opts.Logger.Error("duplicate initialized notification") return nil, fmt.Errorf("duplicate %q received", notificationInitialized) } - if ss.server.opts.KeepAlive > 0 { - ss.startKeepalive(ss.server.opts.KeepAlive) - } if h := ss.server.opts.InitializedHandler; h != nil { h(ctx, serverRequestFor(ss, params)) } @@ -1110,7 +1114,7 @@ type ServerSession struct { server *Server conn *jsonrpc2.Connection mcpConn Connection - keepaliveCancel context.CancelFunc // TODO: theory around why keepaliveCancel need not be guarded + keepaliveCancel context.CancelFunc mu sync.Mutex state ServerSessionState @@ -1504,7 +1508,8 @@ func (ss *ServerSession) setLevel(_ context.Context, params *SetLoggingLevelPara func (ss *ServerSession) Close() error { if ss.keepaliveCancel != nil { // Note: keepaliveCancel access is safe without a mutex because: - // 1. keepaliveCancel is only written once during startKeepalive (happens-before all Close calls) + // 1. keepaliveCancel is only written once during Server.Connect (through startKeepalive), + // which happens before any code that may call Close from another goroutine // 2. context.CancelFunc is safe to call multiple times and from multiple goroutines // 3. The keepalive goroutine calls Close on ping failure, but this is safe since // Close is idempotent and conn.Close() handles concurrent calls correctly @@ -1526,7 +1531,7 @@ func (ss *ServerSession) Wait() error { // startKeepalive starts the keepalive mechanism for this server session. func (ss *ServerSession) startKeepalive(interval time.Duration) { - startKeepalive(ss, interval, &ss.keepaliveCancel) + startKeepalive(ss, interval, &ss.keepaliveCancel, ss.server.opts.Logger) } // pageToken is the internal structure for the opaque pagination cursor. diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/shared.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/shared.go index 5662b121f..b5b09db2e 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/shared.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/shared.go @@ -14,6 +14,7 @@ package mcp import ( "context" "encoding/json" + "errors" "fmt" "log/slog" "net/http" @@ -35,6 +36,7 @@ const ( // It is the version that the client sends in the initialization request, and // the default version used by the server. latestProtocolVersion = protocolVersion20251125 + protocolVersion20260630 = "2026-06-30" protocolVersion20251125 = "2025-11-25" protocolVersion20250618 = "2025-06-18" protocolVersion20250326 = "2025-03-26" @@ -342,6 +344,9 @@ func clientSessionMethod[P Params, R Result](f func(*ClientSession, context.Cont // MCP-specific error codes. const ( + // CodeHeaderMismatch indicates that HTTP headers do not match the corresponding values + // in the request body, or that required headers are missing or malformed. + CodeHeaderMismatch = -32001 // CodeResourceNotFound indicates that a requested resource could not be found. CodeResourceNotFound = -32002 // CodeURLElicitationRequired indicates that the server requires URL elicitation @@ -582,7 +587,10 @@ type keepaliveSession interface { // startKeepalive starts the keepalive mechanism for a session. // It assigns the cancel function to the provided cancelPtr and starts a goroutine // that sends ping messages at the specified interval. -func startKeepalive(session keepaliveSession, interval time.Duration, cancelPtr *context.CancelFunc) { +// +// logger must be non-nil; ping failures (which terminate the keepalive loop and +// close the session) are reported via logger so they are not silently dropped. +func startKeepalive(session keepaliveSession, interval time.Duration, cancelPtr *context.CancelFunc, logger *slog.Logger) { ctx, cancel := context.WithCancel(context.Background()) // Assign cancel function before starting goroutine to avoid race condition. // We cannot return it because the caller may need to cancel during the @@ -602,7 +610,13 @@ func startKeepalive(session keepaliveSession, interval time.Duration, cancelPtr err := session.Ping(pingCtx, nil) pingCancel() if err != nil { - // Ping failed, close the session + if errors.Is(err, jsonrpc2.ErrMethodNotFound) { + // Peer doesn't support ping, stop the keepalive process. + return + } + // Ping failed; log it before closing the session so the + // failure is observable to operators. See #218. + logger.Error("keepalive ping failed; closing session", "error", err) _ = session.Close() return } diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/sse.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/sse.go index e57dad102..0e1ad79ea 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/sse.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/sse.go @@ -10,11 +10,14 @@ import ( "crypto/rand" "fmt" "io" + "mime" + "net" "net/http" "net/url" "sync" "github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2" + "github.com/modelcontextprotocol/go-sdk/internal/util" "github.com/modelcontextprotocol/go-sdk/jsonrpc" ) @@ -52,9 +55,17 @@ type SSEHandler struct { } // SSEOptions specifies options for an [SSEHandler]. -// for now, it is empty, but may be extended in future. -// https://github.com/modelcontextprotocol/go-sdk/issues/507 -type SSEOptions struct{} +type SSEOptions struct { + // DisableLocalhostProtection disables automatic DNS rebinding protection. + // By default, requests arriving via a localhost address (127.0.0.1, [::1]) + // that have a non-localhost Host header are rejected with 403 Forbidden. + // This protects against DNS rebinding attacks regardless of whether the + // server is listening on localhost specifically or on 0.0.0.0. + // + // Only disable this if you understand the security implications. + // See: https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise + DisableLocalhostProtection bool +} // NewSSEHandler returns a new [SSEHandler] that creates and manages MCP // sessions created via incoming HTTP requests. @@ -179,9 +190,27 @@ func (t *SSEServerTransport) Connect(context.Context) (Connection, error) { } func (h *SSEHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - sessionID := req.URL.Query().Get("sessionid") + // DNS rebinding protection: auto-enabled for localhost servers. + // See: https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise + if !h.opts.DisableLocalhostProtection && disablelocalhostprotection != "1" { + if localAddr, ok := req.Context().Value(http.LocalAddrContextKey).(net.Addr); ok && localAddr != nil { + if util.IsLoopback(localAddr.String()) && !util.IsLoopback(req.Host) { + http.Error(w, fmt.Sprintf("Forbidden: invalid Host header %q", req.Host), http.StatusForbidden) + return + } + } + } - // TODO: consider checking Content-Type here. For now, we are lax. + // Validate 'Content-Type' header. + if req.Method == http.MethodPost { + mediaType, _, err := mime.ParseMediaType(req.Header.Get("Content-Type")) + if err != nil || mediaType != "application/json" { + http.Error(w, "Content-Type must be 'application/json'", http.StatusUnsupportedMediaType) + return + } + } + + sessionID := req.URL.Query().Get("sessionid") // For POST requests, the message body is a message to send to a session. if req.Method == http.MethodPost { diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/streamable.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/streamable.go index b0b17956b..708b13263 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/streamable.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/streamable.go @@ -20,6 +20,7 @@ import ( "maps" "math" "math/rand/v2" + "mime" "net" "net/http" "slices" @@ -38,12 +39,6 @@ import ( "github.com/modelcontextprotocol/go-sdk/jsonrpc" ) -const ( - protocolVersionHeader = "Mcp-Protocol-Version" - sessionIDHeader = "Mcp-Session-Id" - lastEventIDHeader = "Last-Event-ID" -) - // A StreamableHTTPHandler is an http.Handler that serves streamable MCP // sessions, as defined by the [MCP spec]. // @@ -179,9 +174,15 @@ type StreamableHTTPOptions struct { // CrossOriginProtection allows to customize cross-origin protection. // The deny handler set in the CrossOriginProtection through SetDenyHandler // is ignored. - // If nil, default (zero-value) cross-origin protection will be used. - // Use `disablecrossoriginprotection` MCPGODEBUG compatibility parameter - // to disable the default protection until v1.6.0. + // If nil, no cross-origin protection is applied. Use the `enableoriginverification` + // MCPGODEBUG compatibility parameter to enable the default protection until v1.8.0. + // + // Deprecated: wrap the handler with cross-origin protection middleware + // instead. For example: + // + // handler := mcp.NewStreamableHTTPHandler(...) + // protection := http.NewCrossOriginProtection() + // protectedHandler := protection.Handler(handler) CrossOriginProtection *http.CrossOriginProtection } @@ -201,7 +202,7 @@ func NewStreamableHTTPHandler(getServer func(*http.Request) *Server, opts *Strea h.opts.Logger = ensureLogger(h.opts.Logger) - if h.opts.CrossOriginProtection == nil { + if h.opts.CrossOriginProtection == nil && enableoriginverification == "1" { h.opts.CrossOriginProtection = &http.CrossOriginProtection{} } @@ -234,15 +235,16 @@ func (h *StreamableHTTPHandler) closeAll() { // disablelocalhostprotection is a compatibility parameter that allows to disable // DNS rebinding protection, which was added in the 1.4.0 version of the SDK. // See the documentation for the mcpgodebug package for instructions how to enable it. -// The option will be removed in the 1.6.0 version of the SDK. +// The option will be removed in the 1.8.0 version of the SDK. var disablelocalhostprotection = mcpgodebug.Value("disablelocalhostprotection") -// disablecrossoriginprotection is a compatibility parameter that allows to disable -// the verification of the 'Origin' and 'Content-Type' headers, which was added in -// the 1.4.1 version of the SDK. See the documentation for the mcpgodebug package -// for instructions how to enable it. -// The option will be removed in the 1.6.0 version of the SDK. -var disablecrossoriginprotection = mcpgodebug.Value("disablecrossoriginprotection") +// enableoriginverification is a compatibility parameter that restores the +// default cross-origin protection behavior from v1.4.1-v1.5.0. When set to +// "1", a zero-value CrossOriginProtection will be applied if none is +// explicitly provided in StreamableHTTPOptions. +// See the documentation for the mcpgodebug package for instructions how to enable it. +// The option will be removed in the 1.8.0 version of the SDK. +var enableoriginverification = mcpgodebug.Value("enableoriginverification") func (h *StreamableHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // DNS rebinding protection: auto-enabled for localhost servers. @@ -256,20 +258,18 @@ func (h *StreamableHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Reque } } - if disablecrossoriginprotection != "1" { + if h.opts.CrossOriginProtection != nil { // Verify the 'Origin' header to protect against CSRF attacks. if err := h.opts.CrossOriginProtection.Check(req); err != nil { http.Error(w, err.Error(), http.StatusForbidden) return } - // Validate 'Content-Type' header. - if req.Method == http.MethodPost { - contentType := req.Header.Get("Content-Type") - if contentType != "application/json" { - http.Error(w, "Content-Type must be 'application/json'", http.StatusUnsupportedMediaType) - return - } - } + } + + // Validate 'Content-Type' header. + if req.Method == http.MethodPost && baseMediaType(req.Header.Get("Content-Type")) != "application/json" { + http.Error(w, "Content-Type must be 'application/json'", http.StatusUnsupportedMediaType) + return } // Allow multiple 'Accept' headers. @@ -559,6 +559,14 @@ func streamableAccepts(values []string) (jsonOK, streamOK bool) { return jsonOK, streamOK } +func baseMediaType(value string) string { + mediaType, _, err := mime.ParseMediaType(value) + if err != nil { + return "" + } + return mediaType +} + // A StreamableServerTransport implements the server side of the MCP streamable // transport. // @@ -1177,6 +1185,24 @@ func (c *streamableServerConn) servePOST(w http.ResponseWriter, req *http.Reques } } + // Validate MCP standard headers (Mcp-Method, Mcp-Name) + if !isBatch && len(incoming) == 1 { + if err := validateMcpHeaders(req.Header, incoming[0]); err != nil { + resp := &jsonrpc.Response{ + Error: jsonrpc2.NewError(CodeHeaderMismatch, err.Error()), + } + if jreq, ok := incoming[0].(*jsonrpc.Request); ok { + resp.ID = jreq.ID + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + if data, err := jsonrpc2.EncodeMessage(resp); err == nil { + w.Write(data) + } + return + } + } + // The prime and close events were added in protocol version 2025-11-25 (SEP-1699). // Use the version from InitializeParams if this is an initialize request, // otherwise use the protocol version header. @@ -1670,7 +1696,7 @@ func (c *streamableClientConn) connectStandaloneSSE() { resp.Body.Close() return } - if resp.Header.Get("Content-Type") != "text/event-stream" { + if baseMediaType(resp.Header.Get("Content-Type")) != "text/event-stream" { // modelcontextprotocol/go-sdk#736: some servers return 200 OK or redirect with // non-SSE content type instead of text/event-stream for the standalone // SSE stream. @@ -1781,14 +1807,17 @@ func (c *streamableClientConn) Write(ctx context.Context, msg jsonrpc.Message) e // Failure to set headers means that the request was not sent. // Wrap with ErrRejected so the jsonrpc2 connection doesn't set writeErr // and permanently break the connection. - return nil, nil, fmt.Errorf("%s: %w: %v", requestSummary, jsonrpc2.ErrRejected, err) + return nil, nil, fmt.Errorf("%s: %w: %w", requestSummary, jsonrpc2.ErrRejected, err) } + // Keep this after the setMCPHeaders call to ensure that the + // protocol version header is set. + setStandardHeaders(req.Header, msg) resp, err := c.client.Do(req) if err != nil { // Any error from client.Do means the request didn't reach the server. // Wrap with ErrRejected so the jsonrpc2 connection doesn't set writeErr // and permanently break the connection. - err = fmt.Errorf("%s: %w: %v", requestSummary, jsonrpc2.ErrRejected, err) + err = fmt.Errorf("%s: %w: %w", requestSummary, jsonrpc2.ErrRejected, err) } return req, resp, err } @@ -1800,6 +1829,22 @@ func (c *streamableClientConn) Write(ctx context.Context, msg jsonrpc.Message) e if (resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden) && c.oauthHandler != nil { if err := c.oauthHandler.Authorize(ctx, req, resp); err != nil { + // If the caller's context was cancelled while we were running the + // authorization flow, treat the connection as failed so subsequent + // operations on it (e.g. the cancellation notify the call layer + // sends in response to ctx cancellation) short-circuit instead of + // re-invoking the OAuth handler. Otherwise the user gets prompted + // to authorize a request they have already abandoned. See #882. + // + // We check ctx.Err() rather than the error returned by Authorize, + // because the handler is user-implemented and may return an error + // that does not wrap context.Canceled (e.g. a custom sentinel or + // a fmt.Errorf with %v). The context itself is the authoritative + // source for whether the caller abandoned the request. + ctxErr := ctx.Err() + if errors.Is(ctxErr, context.Canceled) || errors.Is(ctxErr, context.DeadlineExceeded) { + c.fail(fmt.Errorf("%s: authorization cancelled: %w", requestSummary, err)) + } // Wrap with ErrRejected so the jsonrpc2 connection doesn't set writeErr // and permanently break the connection. // Wrap the authorization error as well for client inspection. @@ -1854,7 +1899,7 @@ func (c *streamableClientConn) Write(ctx context.Context, msg jsonrpc.Message) e return nil } - contentType := strings.TrimSpace(strings.SplitN(resp.Header.Get("Content-Type"), ";", 2)[0]) + contentType := baseMediaType(resp.Header.Get("Content-Type")) switch contentType { case "application/json": go c.handleJSON(requestSummary, resp) @@ -1902,6 +1947,7 @@ func (c *streamableClientConn) setMCPHeaders(req *http.Request) error { if c.sessionID != "" { req.Header.Set(sessionIDHeader, c.sessionID) } + return nil } diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/streamable_headers.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/streamable_headers.go new file mode 100644 index 000000000..ac9a4121e --- /dev/null +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/streamable_headers.go @@ -0,0 +1,98 @@ +// 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. + +package mcp + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + + internaljson "github.com/modelcontextprotocol/go-sdk/internal/json" + "github.com/modelcontextprotocol/go-sdk/jsonrpc" +) + +const ( + protocolVersionHeader = "Mcp-Protocol-Version" + sessionIDHeader = "Mcp-Session-Id" + lastEventIDHeader = "Last-Event-ID" + methodHeader = "Mcp-Method" + nameHeader = "Mcp-Name" + minVersionForStandardHeaders = protocolVersion20260630 +) + +func extractName(method string, params json.RawMessage) (string, bool) { + switch method { + case "tools/call": + var p CallToolParams + if err := internaljson.Unmarshal(params, &p); err == nil { + return p.Name, true + } + case "prompts/get": + var p GetPromptParams + if err := internaljson.Unmarshal(params, &p); err == nil { + return p.Name, true + } + case "resources/read": + var p ReadResourceParams + if err := internaljson.Unmarshal(params, &p); err == nil { + return p.URI, true + } + } + + return "", false +} + +// setStandardHeaders populates standard MCP headers. +// It requires the protocol version header to be set. +func setStandardHeaders(header http.Header, msg jsonrpc.Message) { + if msg == nil { + return + } + if header.Get(protocolVersionHeader) == "" || header.Get(protocolVersionHeader) < minVersionForStandardHeaders { + return + } + + switch msg := msg.(type) { + case *jsonrpc.Request: + header.Set(methodHeader, msg.Method) + if name, ok := extractName(msg.Method, msg.Params); ok { + header.Set(nameHeader, name) + } + } +} + +func validateMcpHeaders(header http.Header, msg jsonrpc.Message) error { + protocolVersion := header.Get(protocolVersionHeader) + if protocolVersion == "" || protocolVersion < minVersionForStandardHeaders { + return nil + } + + switch msg := msg.(type) { + case *jsonrpc.Request: + methodInHeader := header.Get(methodHeader) + if methodInHeader == "" { + return errors.New("missing required Mcp-Method header") + } + if methodInHeader != msg.Method { + return fmt.Errorf("header mismatch: Mcp-Method header value '%s' does not match body value '%s'", methodInHeader, msg.Method) + } + + if msg.Method == "tools/call" || msg.Method == "resources/read" || msg.Method == "prompts/get" { + nameInHeader := header.Get(nameHeader) + if nameInHeader == "" { + return fmt.Errorf("missing required Mcp-Name header for method %q", msg.Method) + } + nameInBody, ok := extractName(msg.Method, msg.Params) + if !ok { + return fmt.Errorf("failed to extract name from parameters for method %q", msg.Method) + } + if nameInHeader != nameInBody { + return fmt.Errorf("header mismatch: Mcp-Name header value '%s' does not match body value '%s'", nameInHeader, nameInBody) + } + } + } + return nil +} diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/transport.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/transport.go index 23dccf8e1..ea4474787 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/transport.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/mcp/transport.go @@ -10,17 +10,24 @@ import ( "errors" "fmt" "io" - "log" + "log/slog" "net" "os" "sync" + "time" internaljson "github.com/modelcontextprotocol/go-sdk/internal/json" "github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2" - "github.com/modelcontextprotocol/go-sdk/internal/xcontext" "github.com/modelcontextprotocol/go-sdk/jsonrpc" ) +// notifyCancellationTimeout bounds the cancellation notification we send to +// the peer when the caller's context is cancelled. The notification is +// best-effort: a degraded connection (e.g. an OAuth flow that has been +// abandoned) must not be able to block the caller's return path or +// re-trigger expensive recovery on its behalf. See issue #882. +const notifyCancellationTimeout = 5 * time.Second + // ErrConnectionClosed is returned when sending a message to a connection that // is closed or in the process of closing. var ErrConnectionClosed = errors.New("connection closed") @@ -152,7 +159,9 @@ type handler interface { handle(ctx context.Context, req *jsonrpc.Request) (any, error) } -func connect[H handler, State any](ctx context.Context, t Transport, b binder[H, State], s State, onClose func()) (H, error) { +// connect wires a transport to a binder. logger must be non-nil; it receives +// jsonrpc2 internal errors that would otherwise be dropped (see #218). +func connect[H handler, State any](ctx context.Context, t Transport, b binder[H, State], s State, onClose func(), logger *slog.Logger) (H, error) { var zero H mcpConn, err := t.Connect(ctx) if err != nil { @@ -178,7 +187,9 @@ func connect[H handler, State any](ctx context.Context, t Transport, b binder[H, OnDone: func() { b.disconnect(h) }, - OnInternalError: func(err error) { log.Printf("jsonrpc2 error: %v", err) }, + OnInternalError: func(err error) { + logger.Error("jsonrpc2 internal error", "error", err) + }, }) assert(preempter.conn != nil, "unbound preempter") return h, nil @@ -216,8 +227,9 @@ func call(ctx context.Context, conn *jsonrpc2.Connection, method string, params case errors.Is(err, jsonrpc2.ErrClientClosing), errors.Is(err, jsonrpc2.ErrServerClosing): return fmt.Errorf("%w: calling %q: %v", ErrConnectionClosed, method, err) case ctx.Err() != nil: - // Notify the peer of cancellation. - err := conn.Notify(xcontext.Detach(ctx), notificationCancelled, &CancelledParams{ + notifyCtx, cancelNotify := context.WithTimeout(context.WithoutCancel(ctx), notifyCancellationTimeout) + defer cancelNotify() + err := conn.Notify(notifyCtx, notificationCancelled, &CancelledParams{ Reason: ctx.Err().Error(), RequestID: call.ID().Raw(), }) diff --git a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/oauthex/dcr.go b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/oauthex/dcr.go index 653cf5b11..f46d0e8e7 100644 --- a/mcp/vendor/github.com/modelcontextprotocol/go-sdk/oauthex/dcr.go +++ b/mcp/vendor/github.com/modelcontextprotocol/go-sdk/oauthex/dcr.go @@ -89,6 +89,11 @@ type ClientRegistrationMetadata struct { // SoftwareStatement is an OPTIONAL JWT that asserts client metadata values. // Values in the software statement take precedence over other metadata values. SoftwareStatement string `json:"software_statement,omitempty"` + + // ApplicationType is an OPTIONAL string that indicates the type of application. + // Valid values are "native" and "web". + // If omitted, OIDC-compliant authorization servers default to "web". + ApplicationType string `json:"application_type,omitempty"` } // ClientRegistrationResponse represents the fields returned by the Authorization Server @@ -204,7 +209,7 @@ func RegisterClient(ctx context.Context, registrationEndpoint string, clientMeta return nil, fmt.Errorf("failed to read registration response body: %w", err) } - if resp.StatusCode == http.StatusCreated { + if resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusOK { var regResponse ClientRegistrationResponse if err := internaljson.Unmarshal(body, ®Response); err != nil { return nil, fmt.Errorf("failed to decode successful registration response: %w (%s)", err, string(body)) diff --git a/mcp/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go b/mcp/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go index 53f814d7a..6c7c5bfd5 100644 --- a/mcp/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go +++ b/mcp/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !darwin && !linux && !netbsd && !openbsd && arm64 +//go:build !darwin && !linux && !netbsd && !openbsd && !windows && arm64 package cpu diff --git a/mcp/vendor/golang.org/x/sys/cpu/cpu_windows.go b/mcp/vendor/golang.org/x/sys/cpu/cpu_windows.go new file mode 100644 index 000000000..99ec8fdfc --- /dev/null +++ b/mcp/vendor/golang.org/x/sys/cpu/cpu_windows.go @@ -0,0 +1,26 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +//go:generate go run golang.org/x/sys/windows/mkwinsyscall -systemdll=false -output zcpu_windows.go cpu_windows.go + +//sys isProcessorFeaturePresent(ProcessorFeature uint32) (ret bool) = kernel32.IsProcessorFeaturePresent + +// The processor features to be tested for IsProcessorFeaturePresent, see +// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent +const ( + _PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30 + _PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE = 31 + _PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE = 34 + _PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE = 43 + + _PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE = 44 + _PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE = 45 + _PF_ARM_SVE_INSTRUCTIONS_AVAILABLE = 46 + _PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE = 47 + + _PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE = 64 + _PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE = 65 +) diff --git a/mcp/vendor/golang.org/x/sys/cpu/cpu_windows_arm64.go b/mcp/vendor/golang.org/x/sys/cpu/cpu_windows_arm64.go new file mode 100644 index 000000000..034732e55 --- /dev/null +++ b/mcp/vendor/golang.org/x/sys/cpu/cpu_windows_arm64.go @@ -0,0 +1,38 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +func doinit() { + // set HasASIMD and HasFP to true as per + // https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#base-requirements + // + // The ARM64 version of Windows always presupposes that it's running on an ARMv8 or later architecture. + // Both floating-point and NEON support are presumed to be present in hardware. + // + ARM64.HasASIMD = true + ARM64.HasFP = true + + if isProcessorFeaturePresent(_PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) { + ARM64.HasAES = true + ARM64.HasPMULL = true + ARM64.HasSHA1 = true + ARM64.HasSHA2 = true + } + ARM64.HasSHA3 = isProcessorFeaturePresent(_PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE) + ARM64.HasCRC32 = isProcessorFeaturePresent(_PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) + ARM64.HasSHA512 = isProcessorFeaturePresent(_PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE) + ARM64.HasATOMICS = isProcessorFeaturePresent(_PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) + if isProcessorFeaturePresent(_PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) { + ARM64.HasASIMDDP = true + ARM64.HasASIMDRDM = true + } + if isProcessorFeaturePresent(_PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) { + ARM64.HasLRCPC = true + ARM64.HasSM3 = true + } + ARM64.HasSVE = isProcessorFeaturePresent(_PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + ARM64.HasSVE2 = isProcessorFeaturePresent(_PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) + ARM64.HasJSCVT = isProcessorFeaturePresent(_PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE) +} diff --git a/mcp/vendor/golang.org/x/sys/cpu/zcpu_windows.go b/mcp/vendor/golang.org/x/sys/cpu/zcpu_windows.go new file mode 100644 index 000000000..6411a7a71 --- /dev/null +++ b/mcp/vendor/golang.org/x/sys/cpu/zcpu_windows.go @@ -0,0 +1,48 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package cpu + +import ( + "syscall" + "unsafe" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procIsProcessorFeaturePresent = modkernel32.NewProc("IsProcessorFeaturePresent") +) + +func isProcessorFeaturePresent(ProcessorFeature uint32) (ret bool) { + r0, _, _ := syscall.SyscallN(procIsProcessorFeaturePresent.Addr(), uintptr(ProcessorFeature)) + ret = r0 != 0 + return +} diff --git a/mcp/vendor/modules.txt b/mcp/vendor/modules.txt index 1f3e06b55..a3149e914 100644 --- a/mcp/vendor/modules.txt +++ b/mcp/vendor/modules.txt @@ -9,13 +9,13 @@ github.com/goccy/go-yaml/parser github.com/goccy/go-yaml/printer github.com/goccy/go-yaml/scanner github.com/goccy/go-yaml/token -# github.com/google/jsonschema-go v0.4.2 +# github.com/google/jsonschema-go v0.4.3 ## explicit; go 1.23.0 github.com/google/jsonschema-go/jsonschema # github.com/inconshreveable/mousetrap v1.1.0 ## explicit; go 1.18 github.com/inconshreveable/mousetrap -# github.com/modelcontextprotocol/go-sdk v1.5.0 +# github.com/modelcontextprotocol/go-sdk v1.6.0 ## explicit; go 1.25.0 github.com/modelcontextprotocol/go-sdk/auth github.com/modelcontextprotocol/go-sdk/internal/json @@ -58,7 +58,7 @@ github.com/yosida95/uritemplate/v3 ## explicit; go 1.25.0 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sys v0.43.0 +# golang.org/x/sys v0.44.0 ## explicit; go 1.25.0 golang.org/x/sys/cpu # github.com/netapp/harvest/v2 => ../ diff --git a/vendor/golang.org/x/sys/unix/affinity_linux.go b/vendor/golang.org/x/sys/unix/affinity_linux.go index 3ea470387..acd6257fa 100644 --- a/vendor/golang.org/x/sys/unix/affinity_linux.go +++ b/vendor/golang.org/x/sys/unix/affinity_linux.go @@ -13,11 +13,19 @@ import ( const cpuSetSize = _CPU_SETSIZE / _NCPUBITS -// CPUSet represents a CPU affinity mask. +// CPUSet represents a bit mask of CPUs, to be used with [SchedGetaffinity], [SchedSetaffinity], +// and [SetMemPolicy]. +// +// Note this type can only represent CPU IDs 0 through 1023. +// Use [CPUSetDynamic]/[NewCPUSet] instead to avoid this limit. type CPUSet [cpuSetSize]cpuMask -func schedAffinity(trap uintptr, pid int, set *CPUSet) error { - _, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set))) +// CPUSetDynamic represents a bit mask of CPUs, to be used with [SchedGetaffinityDynamic], +// [SchedSetaffinityDynamic], and [SetMemPolicyDynamic]. Use [NewCPUSet] to allocate. +type CPUSetDynamic []cpuMask + +func schedAffinity(trap uintptr, pid int, size uintptr, ptr unsafe.Pointer) error { + _, _, e := RawSyscall(trap, uintptr(pid), uintptr(size), uintptr(ptr)) if e != 0 { return errnoErr(e) } @@ -27,13 +35,13 @@ func schedAffinity(trap uintptr, pid int, set *CPUSet) error { // SchedGetaffinity gets the CPU affinity mask of the thread specified by pid. // If pid is 0 the calling thread is used. func SchedGetaffinity(pid int, set *CPUSet) error { - return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set) + return schedAffinity(SYS_SCHED_GETAFFINITY, pid, unsafe.Sizeof(*set), unsafe.Pointer(set)) } // SchedSetaffinity sets the CPU affinity mask of the thread specified by pid. // If pid is 0 the calling thread is used. func SchedSetaffinity(pid int, set *CPUSet) error { - return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set) + return schedAffinity(SYS_SCHED_SETAFFINITY, pid, unsafe.Sizeof(*set), unsafe.Pointer(set)) } // Zero clears the set s, so that it contains no CPUs. @@ -45,9 +53,7 @@ func (s *CPUSet) Zero() { // will silently ignore any invalid CPU bits in [CPUSet] so this is an // efficient way of resetting the CPU affinity of a process. func (s *CPUSet) Fill() { - for i := range s { - s[i] = ^cpuMask(0) - } + cpuMaskFill(s[:]) } func cpuBitsIndex(cpu int) int { @@ -58,24 +64,27 @@ func cpuBitsMask(cpu int) cpuMask { return cpuMask(1 << (uint(cpu) % _NCPUBITS)) } -// Set adds cpu to the set s. -func (s *CPUSet) Set(cpu int) { +func cpuMaskFill(s []cpuMask) { + for i := range s { + s[i] = ^cpuMask(0) + } +} + +func cpuMaskSet(s []cpuMask, cpu int) { i := cpuBitsIndex(cpu) if i < len(s) { s[i] |= cpuBitsMask(cpu) } } -// Clear removes cpu from the set s. -func (s *CPUSet) Clear(cpu int) { +func cpuMaskClear(s []cpuMask, cpu int) { i := cpuBitsIndex(cpu) if i < len(s) { s[i] &^= cpuBitsMask(cpu) } } -// IsSet reports whether cpu is in the set s. -func (s *CPUSet) IsSet(cpu int) bool { +func cpuMaskIsSet(s []cpuMask, cpu int) bool { i := cpuBitsIndex(cpu) if i < len(s) { return s[i]&cpuBitsMask(cpu) != 0 @@ -83,11 +92,98 @@ func (s *CPUSet) IsSet(cpu int) bool { return false } -// Count returns the number of CPUs in the set s. -func (s *CPUSet) Count() int { +func cpuMaskCount(s []cpuMask) int { c := 0 for _, b := range s { c += bits.OnesCount64(uint64(b)) } return c } + +// Set adds cpu to the set s. If cpu is out of bounds for s, no action is taken. +func (s *CPUSet) Set(cpu int) { + cpuMaskSet(s[:], cpu) +} + +// Clear removes cpu from the set s. If cpu is out of bounds for s, no action is taken. +func (s *CPUSet) Clear(cpu int) { + cpuMaskClear(s[:], cpu) +} + +// IsSet reports whether cpu is in the set s. +func (s *CPUSet) IsSet(cpu int) bool { + return cpuMaskIsSet(s[:], cpu) +} + +// Count returns the number of CPUs in the set s. +func (s *CPUSet) Count() int { + return cpuMaskCount(s[:]) +} + +// NewCPUSet creates a CPU affinity mask capable of representing CPU IDs +// up to maxCPU (exclusive). +func NewCPUSet(maxCPU int) CPUSetDynamic { + numMasks := (maxCPU + _NCPUBITS - 1) / _NCPUBITS + if numMasks == 0 { + numMasks = 1 + } + return make(CPUSetDynamic, numMasks) +} + +// Zero clears the set s, so that it contains no CPUs. +func (s CPUSetDynamic) Zero() { + clear(s) +} + +// Fill adds all possible CPU bits to the set s. On Linux, [SchedSetaffinityDynamic] +// will silently ignore any invalid CPU bits in [CPUSetDynamic] so this is an +// efficient way of resetting the CPU affinity of a process. +func (s CPUSetDynamic) Fill() { + cpuMaskFill(s) +} + +// Set adds cpu to the set s. If cpu is out of bounds for s, no action is taken. +func (s CPUSetDynamic) Set(cpu int) { + cpuMaskSet(s, cpu) +} + +// Clear removes cpu from the set s. If cpu is out of bounds for s, no action is taken. +func (s CPUSetDynamic) Clear(cpu int) { + cpuMaskClear(s, cpu) +} + +// IsSet reports whether cpu is in the set s. +func (s CPUSetDynamic) IsSet(cpu int) bool { + return cpuMaskIsSet(s, cpu) +} + +// Count returns the number of CPUs in the set s. +func (s CPUSetDynamic) Count() int { + return cpuMaskCount(s) +} + +func (s CPUSetDynamic) size() uintptr { + return uintptr(len(s)) * unsafe.Sizeof(cpuMask(0)) +} + +func (s CPUSetDynamic) pointer() unsafe.Pointer { + if len(s) == 0 { + return nil + } + return unsafe.Pointer(&s[0]) +} + +// SchedGetaffinityDynamic gets the CPU affinity mask of the thread specified by pid. +// If pid is 0 the calling thread is used. +// +// If the set is smaller than the size of the affinity mask used by the kernel, +// [EINVAL] is returned. +func SchedGetaffinityDynamic(pid int, set CPUSetDynamic) error { + return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set.size(), set.pointer()) +} + +// SchedSetaffinityDynamic sets the CPU affinity mask of the thread specified by pid. +// If pid is 0 the calling thread is used. +func SchedSetaffinityDynamic(pid int, set CPUSetDynamic) error { + return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set.size(), set.pointer()) +} diff --git a/vendor/golang.org/x/sys/unix/mkall.sh b/vendor/golang.org/x/sys/unix/mkall.sh index d0ed61191..f6ddee1ae 100644 --- a/vendor/golang.org/x/sys/unix/mkall.sh +++ b/vendor/golang.org/x/sys/unix/mkall.sh @@ -51,7 +51,7 @@ if [[ "$GOOS" = "linux" ]]; then # Files generated through docker (use $cmd so you can Ctl-C the build or run) set -e $cmd docker build --tag generate:$GOOS $GOOS - $cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && pwd):/build generate:$GOOS + $cmd docker run --rm --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && pwd):/build generate:$GOOS exit fi diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 06c0eea6f..f7b82bcca 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -2644,8 +2644,12 @@ func SchedGetAttr(pid int, flags uint) (*SchedAttr, error) { //sys Cachestat(fd uint, crange *CachestatRange, cstat *Cachestat_t, flags uint) (err error) //sys Mseal(b []byte, flags uint) (err error) -//sys setMemPolicy(mode int, mask *CPUSet, size int) (err error) = SYS_SET_MEMPOLICY +//sys setMemPolicy(mode int, mask unsafe.Pointer, size uintptr) (err error) = SYS_SET_MEMPOLICY func SetMemPolicy(mode int, mask *CPUSet) error { - return setMemPolicy(mode, mask, _CPU_SETSIZE) + return setMemPolicy(mode, unsafe.Pointer(mask), _CPU_SETSIZE) +} + +func SetMemPolicyDynamic(mode int, mask CPUSetDynamic) error { + return setMemPolicy(mode, mask.pointer(), mask.size()) } diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go index cd2dd797f..ecf92bfa2 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go @@ -82,6 +82,9 @@ func Time(t *Time_t) (Time_t, error) { } func Utime(path string, buf *Utimbuf) error { + if buf == nil { + return Utimes(path, nil) + } tv := []Timeval{ {Sec: buf.Actime}, {Sec: buf.Modtime}, diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go index 745e5c7e6..173738077 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go @@ -113,6 +113,9 @@ func Time(t *Time_t) (Time_t, error) { } func Utime(path string, buf *Utimbuf) error { + if buf == nil { + return Utimes(path, nil) + } tv := []Timeval{ {Sec: buf.Actime}, {Sec: buf.Modtime}, diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go index dd2262a40..a3fd1d0b8 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go @@ -150,6 +150,9 @@ func Time(t *Time_t) (Time_t, error) { } func Utime(path string, buf *Utimbuf) error { + if buf == nil { + return Utimes(path, nil) + } tv := []Timeval{ {Sec: buf.Actime}, {Sec: buf.Modtime}, diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go index 8cf3670bd..fc5543c5f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go @@ -112,6 +112,9 @@ func Time(t *Time_t) (Time_t, error) { } func Utime(path string, buf *Utimbuf) error { + if buf == nil { + return Utimes(path, nil) + } tv := []Timeval{ {Sec: buf.Actime}, {Sec: buf.Modtime}, diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 8935d10a3..886f5de5b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -2241,8 +2241,8 @@ func Mseal(b []byte, flags uint) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setMemPolicy(mode int, mask *CPUSet, size int) (err error) { - _, _, e1 := Syscall(SYS_SET_MEMPOLICY, uintptr(mode), uintptr(unsafe.Pointer(mask)), uintptr(size)) +func setMemPolicy(mode int, mask unsafe.Pointer, size uintptr) (err error) { + _, _, e1 := Syscall(SYS_SET_MEMPOLICY, uintptr(mode), uintptr(mask), uintptr(size)) if e1 != 0 { err = errnoErr(e1) } diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index d76643658..453a7b97f 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -892,9 +892,13 @@ const socket_error = uintptr(^uint32(0)) //sys MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar //sys getBestInterfaceEx(sockaddr unsafe.Pointer, pdwBestIfIndex *uint32) (errcode error) = iphlpapi.GetBestInterfaceEx //sys GetIfEntry2Ex(level uint32, row *MibIfRow2) (errcode error) = iphlpapi.GetIfEntry2Ex +//sys GetIfTable2Ex(level uint32, table **MibIfTable2) (errcode error) = iphlpapi.GetIfTable2Ex //sys GetIpForwardEntry2(row *MibIpForwardRow2) (errcode error) = iphlpapi.GetIpForwardEntry2 //sys GetIpForwardTable2(family uint16, table **MibIpForwardTable2) (errcode error) = iphlpapi.GetIpForwardTable2 +//sys GetIpInterfaceEntry(row *MibIpInterfaceRow) (errcode error) = iphlpapi.GetIpInterfaceEntry +//sys GetIpInterfaceTable(family uint16, table **MibIpInterfaceTable) (errcode error) = iphlpapi.GetIpInterfaceTable //sys GetUnicastIpAddressEntry(row *MibUnicastIpAddressRow) (errcode error) = iphlpapi.GetUnicastIpAddressEntry +//sys GetUnicastIpAddressTable(family uint16, table **MibUnicastIpAddressTable) (errcode error) = iphlpapi.GetUnicastIpAddressTable //sys FreeMibTable(memory unsafe.Pointer) = iphlpapi.FreeMibTable //sys NotifyIpInterfaceChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyIpInterfaceChange //sys NotifyRouteChange2(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyRouteChange2 @@ -1693,10 +1697,13 @@ func NewNTUnicodeString(s string) (*NTUnicodeString, error) { if err != nil { return nil, err } - n := uint16(len(s16) * 2) + n := len(s16) * 2 + if n > (1<<16)-1 { + return nil, syscall.EINVAL + } return &NTUnicodeString{ - Length: n - 2, // subtract 2 bytes for the NULL terminator - MaximumLength: n, + Length: uint16(n) - 2, // subtract 2 bytes for the NULL terminator + MaximumLength: uint16(n), Buffer: &s16[0], }, nil } diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index d5658a138..d82299e33 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -2320,6 +2320,21 @@ type MibIfRow2 struct { OutQLen uint64 } +// MIB_IF_TABLE_LEVEL enumeration from netioapi.h or +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ne-netioapi-mib_if_table_level. +const ( + MibIfTableNormal = 0 + MibIfTableRaw = 1 + MibIfTableNormalWithoutStatistics = 2 +) + +// MibIfTable2 contains a table of logical and physical interface entries. See +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_if_table2. +type MibIfTable2 struct { + NumEntries uint32 + Table [1]MibIfRow2 +} + // IP_ADDRESS_PREFIX stores an IP address prefix. See // https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-ip_address_prefix. type IpAddressPrefix struct { @@ -2413,6 +2428,13 @@ type MibUnicastIpAddressRow struct { CreationTimeStamp Filetime } +// MibUnicastIpAddressTable contains a table of unicast IP address entries. See +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_unicastipaddress_table. +type MibUnicastIpAddressTable struct { + NumEntries uint32 + Table [1]MibUnicastIpAddressRow +} + const ScopeLevelCount = 16 // MIB_IPINTERFACE_ROW stores interface management information for a particular IP address family on a network interface. @@ -2455,6 +2477,13 @@ type MibIpInterfaceRow struct { DisableDefaultRoutes uint8 } +// MibIpInterfaceTable contains a table of IP interface entries. See +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_ipinterface_table. +type MibIpInterfaceTable struct { + NumEntries uint32 + Table [1]MibIpInterfaceRow +} + // Console related constants used for the mode parameter to SetConsoleMode. See // https://docs.microsoft.com/en-us/windows/console/setconsolemode for details. diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index fe7a4ea12..a506ac0f1 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -188,9 +188,13 @@ var ( procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex") + procGetIfTable2Ex = modiphlpapi.NewProc("GetIfTable2Ex") procGetIpForwardEntry2 = modiphlpapi.NewProc("GetIpForwardEntry2") procGetIpForwardTable2 = modiphlpapi.NewProc("GetIpForwardTable2") + procGetIpInterfaceEntry = modiphlpapi.NewProc("GetIpInterfaceEntry") + procGetIpInterfaceTable = modiphlpapi.NewProc("GetIpInterfaceTable") procGetUnicastIpAddressEntry = modiphlpapi.NewProc("GetUnicastIpAddressEntry") + procGetUnicastIpAddressTable = modiphlpapi.NewProc("GetUnicastIpAddressTable") procNotifyIpInterfaceChange = modiphlpapi.NewProc("NotifyIpInterfaceChange") procNotifyRouteChange2 = modiphlpapi.NewProc("NotifyRouteChange2") procNotifyUnicastIpAddressChange = modiphlpapi.NewProc("NotifyUnicastIpAddressChange") @@ -1674,6 +1678,14 @@ func GetIfEntry2Ex(level uint32, row *MibIfRow2) (errcode error) { return } +func GetIfTable2Ex(level uint32, table **MibIfTable2) (errcode error) { + r0, _, _ := syscall.SyscallN(procGetIfTable2Ex.Addr(), uintptr(level), uintptr(unsafe.Pointer(table))) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + func GetIpForwardEntry2(row *MibIpForwardRow2) (errcode error) { r0, _, _ := syscall.SyscallN(procGetIpForwardEntry2.Addr(), uintptr(unsafe.Pointer(row))) if r0 != 0 { @@ -1690,6 +1702,22 @@ func GetIpForwardTable2(family uint16, table **MibIpForwardTable2) (errcode erro return } +func GetIpInterfaceEntry(row *MibIpInterfaceRow) (errcode error) { + r0, _, _ := syscall.SyscallN(procGetIpInterfaceEntry.Addr(), uintptr(unsafe.Pointer(row))) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + +func GetIpInterfaceTable(family uint16, table **MibIpInterfaceTable) (errcode error) { + r0, _, _ := syscall.SyscallN(procGetIpInterfaceTable.Addr(), uintptr(family), uintptr(unsafe.Pointer(table))) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + func GetUnicastIpAddressEntry(row *MibUnicastIpAddressRow) (errcode error) { r0, _, _ := syscall.SyscallN(procGetUnicastIpAddressEntry.Addr(), uintptr(unsafe.Pointer(row))) if r0 != 0 { @@ -1698,6 +1726,14 @@ func GetUnicastIpAddressEntry(row *MibUnicastIpAddressRow) (errcode error) { return } +func GetUnicastIpAddressTable(family uint16, table **MibUnicastIpAddressTable) (errcode error) { + r0, _, _ := syscall.SyscallN(procGetUnicastIpAddressTable.Addr(), uintptr(family), uintptr(unsafe.Pointer(table))) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + func NotifyIpInterfaceChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) { var _p0 uint32 if initialNotification { diff --git a/vendor/modules.txt b/vendor/modules.txt index c0e3d3127..6dfde66f4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -35,7 +35,7 @@ github.com/spf13/pflag # github.com/zekroTJA/timedmap/v2 v2.0.0 ## explicit; go 1.19 github.com/zekroTJA/timedmap/v2 -# golang.org/x/sys v0.43.0 +# golang.org/x/sys v0.44.0 ## explicit; go 1.25.0 golang.org/x/sys/plan9 golang.org/x/sys/unix