From 11f5c933c832845cd37d2e05844c4605bc3ba658 Mon Sep 17 00:00:00 2001 From: Ankitsinghsisodya Date: Wed, 6 May 2026 13:52:45 +0530 Subject: [PATCH 1/3] 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 b43715ee34..9c22f11e03 100644 --- a/pkg/mcp/mcp.go +++ b/pkg/mcp/mcp.go @@ -6,12 +6,12 @@ import ( "strings" "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 @@ -74,11 +74,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), HasPrompts: true, From 15101175dbaa43800e8fec8b7f993e5117fc733d Mon Sep 17 00:00:00 2001 From: Ankitsinghsisodya Date: Fri, 8 May 2026 13:45:46 +0530 Subject: [PATCH 2/3] 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 a34fca3a9f..6dd6c2e739 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 9c22f11e03..491997a084 100644 --- a/pkg/mcp/mcp.go +++ b/pkg/mcp/mcp.go @@ -74,16 +74,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), 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 60d6e34424c18751ca3364d01d95474224bb52e5 Mon Sep 17 00:00:00 2001 From: Ankitsinghsisodya Date: Fri, 8 May 2026 14:08:37 +0530 Subject: [PATCH 3/3] 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 574bdd01e2..01a387d95b 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