From 8499fd1ca5e40e45ee92f75b3138cad0090fa8a6 Mon Sep 17 00:00:00 2001 From: Ankitsinghsisodya Date: Wed, 6 May 2026 13:52:45 +0530 Subject: [PATCH 1/6] Update MCP version handling to use version from pkg/version - Refactor MCP server implementation to retrieve version from the new version package. - Default to "0.0.0+source" if version is not specified. - Clean up version constants for clarity. --- pkg/mcp/mcp.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/mcp/mcp.go b/pkg/mcp/mcp.go index fe912028fe..d9f621481e 100644 --- a/pkg/mcp/mcp.go +++ b/pkg/mcp/mcp.go @@ -8,12 +8,12 @@ import ( "sync/atomic" "github.com/modelcontextprotocol/go-sdk/mcp" + "knative.dev/func/pkg/version" ) const ( - name = "func" - title = "func" - version = "0.1.0" + name = "func" + title = "func" ) // NOTE: Invoking prompts in some interfaces (such as Claude Code) when all @@ -87,11 +87,16 @@ func New(options ...Option) *Server { o(s) } + vers := version.Vers + if vers == "" { + vers = "0.0.0+source" + } + i := mcp.NewServer( &mcp.Implementation{ Name: name, Title: title, - Version: version}, + Version: vers}, &mcp.ServerOptions{ Instructions: instructions(s.readonly.Load()), HasPrompts: true, From 851e84d5e50bd2982c6c7760d2d6c5c691ff4acc Mon Sep 17 00:00:00 2001 From: Ankitsinghsisodya Date: Fri, 8 May 2026 13:45:46 +0530 Subject: [PATCH 2/6] Refactor version handling to utilize new version package - Updated DefaultVersion in root.go to delegate to version.DefaultVers for consistency. - Modified app.go to retrieve the version using version.Get().Original() for accurate versioning. - Adjusted mcp.go to use version.Get().String() for server versioning, ensuring fallback to DefaultVers if necessary. - Added unit tests in version_test.go to verify behavior of version retrieval and fallback logic. --- cmd/root.go | 6 ++-- pkg/app/app.go | 2 +- pkg/mcp/mcp.go | 7 +---- pkg/version/version.go | 25 +++++++++++++++ pkg/version/version_test.go | 63 +++++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 pkg/version/version_test.go diff --git a/cmd/root.go b/cmd/root.go index 9fb4542d90..f7af54edb0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -20,10 +20,12 @@ import ( "knative.dev/func/pkg/config" fn "knative.dev/func/pkg/functions" "knative.dev/func/pkg/k8s" + "knative.dev/func/pkg/version" ) -// DefaultVersion when building source directly (bypassing the Makefile) -const DefaultVersion = "v0.0.0+source" +// DefaultVersion when building source directly (bypassing the Makefile). +// Delegates to version.DefaultVers so the fallback is defined in one place. +const DefaultVersion = version.DefaultVers // DefaultNamespace is the global static default namespace, and is equivalent // to the Kubernetes default namespace. diff --git a/pkg/app/app.go b/pkg/app/app.go index eae55e63a8..9ea836d40f 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -32,7 +32,7 @@ func Main() { cfg := cmd.RootCommandConfig{ Name: "func", Version: cmd.Version{ - Vers: version.Vers, + Vers: version.Get().Original(), Kver: version.Kver, Hash: version.Hash, }} diff --git a/pkg/mcp/mcp.go b/pkg/mcp/mcp.go index d9f621481e..2390e58e09 100644 --- a/pkg/mcp/mcp.go +++ b/pkg/mcp/mcp.go @@ -87,16 +87,11 @@ func New(options ...Option) *Server { o(s) } - vers := version.Vers - if vers == "" { - vers = "0.0.0+source" - } - i := mcp.NewServer( &mcp.Implementation{ Name: name, Title: title, - Version: vers}, + Version: version.Get().String()}, &mcp.ServerOptions{ Instructions: instructions(s.readonly.Load()), HasPrompts: true, diff --git a/pkg/version/version.go b/pkg/version/version.go index 114c9658bc..d4e4894965 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,3 +1,28 @@ package version +import "github.com/Masterminds/semver/v3" + var Vers, Kver, Hash string + +// DefaultVers is the fallback version used when no build-time version was +// injected (e.g. source builds that bypass the Makefile). +const DefaultVers = "v0.0.0+source" + +// Get returns the parsed semver for this binary. When no build-time version +// was injected via ldflags, DefaultVers is used. If the injected string is +// unparseable, DefaultVers is used as a safe fallback. +// String() returns a clean semver without the leading "v" (e.g. "0.0.0+source"), +// suitable for machine-readable consumers such as the MCP server. +// Original() round-trips the injected string verbatim, preserving the leading +// "v" preferred by human-readable output. +func Get() *semver.Version { + s := Vers + if s == "" { + s = DefaultVers + } + v, err := semver.NewVersion(s) // permissive: accepts leading 'v' + if err != nil { + v, _ = semver.NewVersion(DefaultVers) + } + return v +} diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go new file mode 100644 index 0000000000..477ab211c6 --- /dev/null +++ b/pkg/version/version_test.go @@ -0,0 +1,63 @@ +package version_test + +import ( + "testing" + + "knative.dev/func/pkg/version" +) + +// TestGet_Empty verifies that Get returns the DefaultVers fallback when no +// build-time version has been injected (Vers == ""). +func TestGet_Empty(t *testing.T) { + orig := version.Vers + version.Vers = "" + defer func() { version.Vers = orig }() + + v := version.Get() + if v == nil { + t.Fatal("expected non-nil *semver.Version") + } + // String() must be clean semver without a leading 'v' + if got := v.String(); got != "0.0.0+source" { + t.Errorf("String() = %q; want %q", got, "0.0.0+source") + } + // Original() must round-trip the full default string including 'v' + if got := v.Original(); got != "v0.0.0+source" { + t.Errorf("Original() = %q; want %q", got, "v0.0.0+source") + } +} + +// TestGet_InjectedVersion verifies that a build-time version is parsed and +// exposed correctly. +func TestGet_InjectedVersion(t *testing.T) { + orig := version.Vers + version.Vers = "v1.2.3" + defer func() { version.Vers = orig }() + + v := version.Get() + if v == nil { + t.Fatal("expected non-nil *semver.Version") + } + if got := v.String(); got != "1.2.3" { + t.Errorf("String() = %q; want %q", got, "1.2.3") + } + if got := v.Original(); got != "v1.2.3" { + t.Errorf("Original() = %q; want %q", got, "v1.2.3") + } +} + +// TestGet_InvalidFallsBack verifies that an unparseable injected version does +// not panic and falls back to DefaultVers. +func TestGet_InvalidFallsBack(t *testing.T) { + orig := version.Vers + version.Vers = "not-a-semver!!!" + defer func() { version.Vers = orig }() + + v := version.Get() + if v == nil { + t.Fatal("expected non-nil *semver.Version even for invalid input") + } + if got := v.String(); got != "0.0.0+source" { + t.Errorf("String() = %q; want %q on invalid input", got, "0.0.0+source") + } +} From 8b0920fa46fbd7818d3e3b4ce805c95db1784c5c Mon Sep 17 00:00:00 2001 From: Ankitsinghsisodya Date: Fri, 8 May 2026 14:08:37 +0530 Subject: [PATCH 3/6] Update go.mod to include Masterminds/semver/v3 v3.4.0 as a direct dependency - Added Masterminds/semver/v3 v3.4.0 to the require section for version handling. - Removed the indirect reference to Masterminds/semver/v3 v3.4.0 for clarity. --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f9769f8bd7..2a8abd5f14 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/BurntSushi/toml v1.6.0 github.com/Masterminds/semver v1.5.0 + github.com/Masterminds/semver/v3 v3.4.0 github.com/Microsoft/go-winio v0.6.2 github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b @@ -90,7 +91,6 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.2 // indirect github.com/Azure/go-autorest/tracing v0.6.1 // indirect - github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/ProtonMail/go-crypto v1.4.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect From 2a9aac5726e8538568a4185676a5b997edbe1076 Mon Sep 17 00:00:00 2001 From: Ankitsinghsisodya Date: Sun, 17 May 2026 11:14:38 +0530 Subject: [PATCH 4/6] Update healthcheckHandler to retrieve version using version.Get().String() - Modified the healthcheckHandler function in tools_healthcheck.go to call version.Get().String() for accurate version reporting. - This change ensures that the version displayed in the health check output is consistent with the new version handling introduced in previous commits. --- pkg/mcp/tools_healthcheck.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/mcp/tools_healthcheck.go b/pkg/mcp/tools_healthcheck.go index af16fe25c5..5110882302 100644 --- a/pkg/mcp/tools_healthcheck.go +++ b/pkg/mcp/tools_healthcheck.go @@ -4,6 +4,7 @@ import ( "context" "github.com/modelcontextprotocol/go-sdk/mcp" + "knative.dev/func/pkg/version" ) var healthCheckTool = &mcp.Tool{ @@ -21,7 +22,7 @@ func (s *Server) healthcheckHandler(ctx context.Context, r *mcp.CallToolRequest, output = HealthcheckOutput{ Status: "ok", Message: "The MCP server is running!", - Version: version, + Version: version.Get().String(), } return } From 566407ed6afe52a72eafc7bab3d67f07f8424e1a Mon Sep 17 00:00:00 2001 From: Ankitsinghsisodya Date: Thu, 21 May 2026 21:03:16 +0530 Subject: [PATCH 5/6] Refactor MCP version handling to use computed version from Server struct - Updated the MCP server implementation to store the version as a field in the Server struct, ensuring consistent version reporting across health checks and server initialization. - Removed direct calls to version.Get().String() in favor of using the new s.version field, enhancing clarity and maintainability. - Adjusted the go.mod file to remove the unnecessary dependency on Masterminds/semver/v3, streamlining version management. --- go.mod | 1 - pkg/mcp/mcp.go | 4 +++- pkg/mcp/tools_healthcheck.go | 3 +-- pkg/version/version.go | 27 +++++++++++++++++++++------ 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 2a8abd5f14..f22ab9d45a 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/BurntSushi/toml v1.6.0 github.com/Masterminds/semver v1.5.0 - github.com/Masterminds/semver/v3 v3.4.0 github.com/Microsoft/go-winio v0.6.2 github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b diff --git a/pkg/mcp/mcp.go b/pkg/mcp/mcp.go index 2390e58e09..0ceb9547be 100644 --- a/pkg/mcp/mcp.go +++ b/pkg/mcp/mcp.go @@ -24,6 +24,7 @@ const ( type Server struct { OnInit func(context.Context) // Invoked when the server is initialized prefix string // Command prefix ("func" or "kn func") + version string // Computed once at construction; used by healthcheck readonly atomic.Bool // disables deploy and delete when true executor executor transport mcp.Transport // Transport to use (defaults to StdioTransport) @@ -79,6 +80,7 @@ func WithReadonly(readonly bool) Option { func New(options ...Option) *Server { s := &Server{ prefix: "func", + version: version.Get().String(), transport: &mcp.StdioTransport{}, OnInit: func(_ context.Context) {}, } @@ -91,7 +93,7 @@ func New(options ...Option) *Server { &mcp.Implementation{ Name: name, Title: title, - Version: version.Get().String()}, + Version: s.version}, &mcp.ServerOptions{ Instructions: instructions(s.readonly.Load()), HasPrompts: true, diff --git a/pkg/mcp/tools_healthcheck.go b/pkg/mcp/tools_healthcheck.go index 5110882302..3e2b22147f 100644 --- a/pkg/mcp/tools_healthcheck.go +++ b/pkg/mcp/tools_healthcheck.go @@ -4,7 +4,6 @@ import ( "context" "github.com/modelcontextprotocol/go-sdk/mcp" - "knative.dev/func/pkg/version" ) var healthCheckTool = &mcp.Tool{ @@ -22,7 +21,7 @@ func (s *Server) healthcheckHandler(ctx context.Context, r *mcp.CallToolRequest, output = HealthcheckOutput{ Status: "ok", Message: "The MCP server is running!", - Version: version.Get().String(), + Version: s.version, } return } diff --git a/pkg/version/version.go b/pkg/version/version.go index d4e4894965..e28b0b4902 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,6 +1,10 @@ package version -import "github.com/Masterminds/semver/v3" +import ( + "fmt" + + "github.com/Masterminds/semver" +) var Vers, Kver, Hash string @@ -8,6 +12,18 @@ var Vers, Kver, Hash string // injected (e.g. source builds that bypass the Makefile). const DefaultVers = "v0.0.0+source" +// defaultVersion is DefaultVers parsed once at init time. A panic here means +// the DefaultVers constant was changed to an invalid semver string. +var defaultVersion *semver.Version + +func init() { + var err error + defaultVersion, err = semver.NewVersion(DefaultVers) + if err != nil { + panic(fmt.Sprintf("version: DefaultVers constant %q is not valid semver: %v", DefaultVers, err)) + } +} + // Get returns the parsed semver for this binary. When no build-time version // was injected via ldflags, DefaultVers is used. If the injected string is // unparseable, DefaultVers is used as a safe fallback. @@ -16,13 +32,12 @@ const DefaultVers = "v0.0.0+source" // Original() round-trips the injected string verbatim, preserving the leading // "v" preferred by human-readable output. func Get() *semver.Version { - s := Vers - if s == "" { - s = DefaultVers + if Vers == "" { + return defaultVersion } - v, err := semver.NewVersion(s) // permissive: accepts leading 'v' + v, err := semver.NewVersion(Vers) // permissive: accepts leading 'v' if err != nil { - v, _ = semver.NewVersion(DefaultVers) + return defaultVersion } return v } From f7c7f8def924a38f4a9bc99d95e4eb1bd2e6ad20 Mon Sep 17 00:00:00 2001 From: Ankitsinghsisodya Date: Thu, 21 May 2026 21:12:49 +0530 Subject: [PATCH 6/6] Update go.mod to add indirect dependency on Masterminds/semver/v3 v3.4.0 - Included Masterminds/semver/v3 v3.4.0 as an indirect dependency to support version handling. - This change ensures compatibility with the latest versioning practices while maintaining existing functionality. --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index f22ab9d45a..f48130dc91 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/BurntSushi/toml v1.6.0 github.com/Masterminds/semver v1.5.0 + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Microsoft/go-winio v0.6.2 github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b