From 6ed4519fdadc5c0ace434a330cf24c83fc839c56 Mon Sep 17 00:00:00 2001 From: "elvandlie@gmail.com" Date: Thu, 21 May 2026 03:01:04 +0700 Subject: [PATCH 1/3] fix(mcp): add readonly guard to createHandler The createHandler lacks the s.readonly.Load() check that is present in other mutating tools like deployHandler and deleteHandler. Because of this, an MCP client can execute the create command (which writes project scaffolding files to the disk) even when the server is operating in readonly mode. Added the guard to match the behavior of other tools. Fixes #3810 --- pkg/mcp/tools_create.go | 4 ++++ pkg/mcp/tools_create_test.go | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/pkg/mcp/tools_create.go b/pkg/mcp/tools_create.go index 28e4b7a03e..97b4054ee9 100644 --- a/pkg/mcp/tools_create.go +++ b/pkg/mcp/tools_create.go @@ -20,6 +20,10 @@ var createTool = &mcp.Tool{ } func (s *Server) createHandler(ctx context.Context, r *mcp.CallToolRequest, input CreateInput) (result *mcp.CallToolResult, output CreateOutput, err error) { + if s.readonly.Load() { + err = fmt.Errorf("the server is currently in readonly mode. Please set FUNC_ENABLE_MCP_WRITE and restart the client") + return + } out, err := s.executor.Execute(ctx, "create", input.Args()...) if err != nil { err = fmt.Errorf("%w\n%s", err, string(out)) diff --git a/pkg/mcp/tools_create_test.go b/pkg/mcp/tools_create_test.go index d37b284864..d730857272 100644 --- a/pkg/mcp/tools_create_test.go +++ b/pkg/mcp/tools_create_test.go @@ -123,3 +123,25 @@ func TestTool_Create_BinaryFailure(t *testing.T) { t.Errorf("expected error to include binary output, got: %s", resultToString(result)) } } + +// TestTool_Create_Readonly ensures the create tool rejects requests in readonly mode. +func TestTool_Create_Readonly(t *testing.T) { + client, _, err := newTestPairWithReadonly(t, true) // readonly = true + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "create", + Arguments: map[string]any{"language": "go", "path": "."}, + }) + if err != nil { + t.Fatal(err) + } + if !result.IsError { + t.Fatal("expected create to be rejected in readonly mode") + } + if !strings.Contains(resultToString(result), "readonly mode") { + t.Errorf("expected readonly error message, got: %s", resultToString(result)) + } +} From 35a061b792dba5366581b85b6d437213bd7ac409 Mon Sep 17 00:00:00 2001 From: Tempris Admin Date: Fri, 26 Jun 2026 16:23:26 +0700 Subject: [PATCH 2/3] align create readonly error message --- pkg/mcp/tools_create.go | 2 +- pkg/mcp/tools_create_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/mcp/tools_create.go b/pkg/mcp/tools_create.go index 97b4054ee9..271395a045 100644 --- a/pkg/mcp/tools_create.go +++ b/pkg/mcp/tools_create.go @@ -21,7 +21,7 @@ var createTool = &mcp.Tool{ func (s *Server) createHandler(ctx context.Context, r *mcp.CallToolRequest, input CreateInput) (result *mcp.CallToolResult, output CreateOutput, err error) { if s.readonly.Load() { - err = fmt.Errorf("the server is currently in readonly mode. Please set FUNC_ENABLE_MCP_WRITE and restart the client") + err = fmt.Errorf("the server is currently in read-only mode; to enable write operations, set FUNC_ENABLE_MCP_WRITE in the server environment and restart the server") return } out, err := s.executor.Execute(ctx, "create", input.Args()...) diff --git a/pkg/mcp/tools_create_test.go b/pkg/mcp/tools_create_test.go index d730857272..014dd84a95 100644 --- a/pkg/mcp/tools_create_test.go +++ b/pkg/mcp/tools_create_test.go @@ -141,7 +141,7 @@ func TestTool_Create_Readonly(t *testing.T) { if !result.IsError { t.Fatal("expected create to be rejected in readonly mode") } - if !strings.Contains(resultToString(result), "readonly mode") { + if !strings.Contains(resultToString(result), "read-only mode") { t.Errorf("expected readonly error message, got: %s", resultToString(result)) } } From 461d9c50cc0af7bb0cca144d8e767dfec7a5c86a Mon Sep 17 00:00:00 2001 From: Tempris Admin Date: Fri, 26 Jun 2026 17:24:42 +0700 Subject: [PATCH 3/3] document create as readonly-disabled --- pkg/mcp/instructions_warning.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/mcp/instructions_warning.md b/pkg/mcp/instructions_warning.md index 98697ddeec..bdb87d167b 100644 --- a/pkg/mcp/instructions_warning.md +++ b/pkg/mcp/instructions_warning.md @@ -7,20 +7,20 @@ The Functions MCP server is currently running in **read-only mode**. **Available operations:** -- Create Functions -- Build Functions - Configure Functions (envs, labels, volumes) - Inspect Functions **Disabled operations:** +- Create Functions +- Build Function images - Deploy to cluster - Delete from cluster -These write operations are disabled to prevent unintended cluster modifications. +These write operations are disabled to prevent unintended local or cluster modifications. ## Enabling Write Mode -If the user needs to deploy or delete Functions, you MUST inform them to enable write mode: +If the user needs to create, build, deploy, or delete Functions, you MUST inform them to enable write mode: 1. Close/exit this application completely 2. Set the environment variable: `FUNC_ENABLE_MCP_WRITE=true`