Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ type StaticConfig struct {
promptsDefined bool // Internal: tracks if prompts were defined in config
promptsMetadata toml.MetaData // Internal: metadata for prompts decoding

// Server instructions to be provided by the MCP server to the MCP client
// This can be used to provide specific instructions on how the client should use the server
ServerInstructions string `toml:"server_instructions,omitempty"`

// Internal: parsed provider configs (not exposed to TOML package)
parsedClusterProviderConfigs map[string]configapi.Extended
// Internal: parsed toolset configs (not exposed to TOML package)
Expand Down
1 change: 1 addition & 0 deletions pkg/mcp/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func NewServer(configuration Configuration) (*Server, error) {
HasResources: false,
HasPrompts: true,
HasTools: true,
Instructions: configuration.ServerInstructions,
}),
}

Expand Down
72 changes: 72 additions & 0 deletions pkg/mcp/mcp_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package mcp

import (
"context"
"net/http"
"net/http/httptest"
"sync"
"testing"

"github.com/containers/kubernetes-mcp-server/internal/test"
"github.com/mark3labs/mcp-go/client"
"github.com/mark3labs/mcp-go/client/transport"
"github.com/stretchr/testify/suite"
)
Expand Down Expand Up @@ -97,3 +100,72 @@ func (s *McpHeadersSuite) TestAuthorizationHeaderPropagation() {
func TestMcpHeaders(t *testing.T) {
suite.Run(t, new(McpHeadersSuite))
}

type ServerInstructionsSuite struct {
BaseMcpSuite
mockServer *test.MockServer
}

func (s *ServerInstructionsSuite) SetupTest() {
s.BaseMcpSuite.SetupTest()
s.mockServer = test.NewMockServer()
s.mockServer.Handle(&test.DiscoveryClientHandler{})
s.Cfg.KubeConfig = s.mockServer.KubeconfigFile(s.T())
}

func (s *ServerInstructionsSuite) TearDownTest() {
s.BaseMcpSuite.TearDownTest()
if s.mockServer != nil {
s.mockServer.Close()
}
}

func (s *ServerInstructionsSuite) TestServerInstructions() {
s.Run("returns empty instructions when not configured", func() {
s.Cfg.ServerInstructions = ""
mcpServer, err := NewServer(Configuration{StaticConfig: s.Cfg})
s.Require().NoError(err)
s.T().Cleanup(mcpServer.Close)

testServer := httptest.NewServer(mcpServer.ServeHTTP())
s.T().Cleanup(testServer.Close)

mcpClient, err := client.NewStreamableHttpClient(testServer.URL+"/mcp", transport.WithContinuousListening())
s.Require().NoError(err)
s.T().Cleanup(func() { _ = mcpClient.Close() })

err = mcpClient.Start(context.Background())
s.Require().NoError(err)

result, err := mcpClient.Initialize(context.Background(), test.McpInitRequest())
s.Require().NoError(err)
s.Empty(result.Instructions, "instructions should be empty when not configured")
})

s.Run("returns configured instructions", func() {
expectedInstructions := "Always use YAML output format for kubectl commands."
s.Cfg.ServerInstructions = expectedInstructions

mcpServer, err := NewServer(Configuration{StaticConfig: s.Cfg})
s.Require().NoError(err)
s.T().Cleanup(mcpServer.Close)

testServer := httptest.NewServer(mcpServer.ServeHTTP())
s.T().Cleanup(testServer.Close)

mcpClient, err := client.NewStreamableHttpClient(testServer.URL+"/mcp", transport.WithContinuousListening())
s.Require().NoError(err)
s.T().Cleanup(func() { _ = mcpClient.Close() })

err = mcpClient.Start(context.Background())
s.Require().NoError(err)

result, err := mcpClient.Initialize(context.Background(), test.McpInitRequest())
s.Require().NoError(err)
s.Equal(expectedInstructions, result.Instructions, "instructions should match configured value")
})
}

func TestServerInstructions(t *testing.T) {
suite.Run(t, new(ServerInstructionsSuite))
}