Skip to content

Commit e6440ec

Browse files
committed
rearchitect tools
apply rearchitecture to remaining tools
1 parent 0e2d11a commit e6440ec

20 files changed

Lines changed: 3205 additions & 793 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
/pkg/functions/testdata/default_home/go
1515
/pkg/functions/testdata/default_home/.cache
1616
/pkg/functions/testdata/migrations/*/.gitignore
17+
/pkg/creds/auth.json
1718

1819
# Go
1920
/templates/go/cloudevents/go.sum

cmd/mcp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ For detailed instructions, see: docs/mcp-integration/integration.md`
177177
cmdPrefix := rootCmd.Use
178178

179179
s := mcp.New(mcp.WithPrefix(cmdPrefix))
180-
if err := s.Start(); err != nil {
180+
if err := s.Start(cmd.Context()); err != nil {
181181
log.Fatalf("Server error: %v", err)
182182
return err
183183
}

pkg/mcp/mcp.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type Server struct {
2121

2222
type tool interface {
2323
desc() *mcp.Tool
24-
handle(context.Context, *mcp.CallToolRequest, string, Executor) (*mcp.CallToolResult, error)
24+
handle(context.Context, toolRequestInterface, string, Executor) (*mcp.CallToolResult, error)
2525
}
2626

2727
type resource interface {
@@ -34,24 +34,20 @@ type prompt interface {
3434
handler(prefix string) mcp.PromptHandler
3535
}
3636

37-
// Option is a functional option for configuring a Server
3837
type Option func(*Server)
3938

40-
// WithPrefix sets the command prefix (e.g., "func" or "kn func")
4139
func WithPrefix(prefix string) Option {
4240
return func(s *Server) {
4341
s.prefix = prefix
4442
}
4543
}
4644

47-
// WithExecutor sets the command executor for the server
4845
func WithExecutor(executor Executor) Option {
4946
return func(s *Server) {
5047
s.executor = executor
5148
}
5249
}
5350

54-
// DefaultCommand is the default function command to use when executing.
5551
const DefaultCommand = "func"
5652

5753
func New(options ...Option) *Server {
@@ -64,14 +60,14 @@ func New(options ...Option) *Server {
6460
}, nil),
6561
tools: []tool{
6662
healthCheck{},
67-
create{},
68-
deploy{},
69-
list{},
70-
build{},
71-
del{},
72-
configVolumes{},
73-
configLabels{},
74-
configEnvs{},
63+
createTool{},
64+
deployTool{},
65+
listTool{},
66+
buildTool{},
67+
deleteTool{},
68+
configVolumesTool{},
69+
configLabelsTool{},
70+
configEnvsTool{},
7571
},
7672
resources: []resource{
7773
rootHelpResource{},
@@ -95,7 +91,6 @@ func New(options ...Option) *Server {
9591
},
9692
}
9793

98-
// Apply functional options
9994
for _, o := range options {
10095
o(s)
10196
}
@@ -115,6 +110,13 @@ func New(options ...Option) *Server {
115110
return s
116111
}
117112

118-
func (s *Server) Start() error {
119-
return s.impl.Run(context.Background(), &mcp.StdioTransport{})
113+
// Start the server in blocking mode using default STDIO transport.
114+
func (s *Server) Start(ctx context.Context) error {
115+
return s.impl.Run(ctx, &mcp.StdioTransport{})
116+
}
117+
118+
// Connect starts the server with a custom context and transport (for testing)
119+
// Non-blocking mode for testing.
120+
func (s *Server) Connect(ctx context.Context, transport mcp.Transport) (*mcp.ServerSession, error) {
121+
return s.impl.Connect(ctx, transport, nil)
120122
}

pkg/mcp/mcp_test.go

Lines changed: 96 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ import (
44
"context"
55
"testing"
66

7-
mcpmock "knative.dev/func/pkg/mcp/mock"
7+
"github.com/modelcontextprotocol/go-sdk/mcp"
8+
"knative.dev/func/pkg/mcp/mock"
89
)
910

10-
// TestServerCreation verifies that the MCP server can be instantiated with custom options
11-
func TestServerCreation(t *testing.T) {
11+
// TestNew ensures that the MCP server can be instantiated with custom options
12+
func TestNew(t *testing.T) {
1213
// Define a mock executor
13-
mock := mcpmock.NewExecutor()
14-
mock.ExecuteFn = func(ctx context.Context, dir string, name string, args ...string) ([]byte, error) {
14+
executor := mock.NewExecutor()
15+
executor.ExecuteFn = func(ctx context.Context, dir string, name string, args ...string) ([]byte, error) {
1516
return []byte("success"), nil
1617
}
1718

1819
// Instantiate the MCP Server with mock executor and custom prefix
19-
server := New(WithPrefix("kn func"), WithExecutor(mock))
20+
server := New(WithPrefix("kn func"), WithExecutor(executor))
2021

2122
// Verify server was created
2223
if server == nil {
@@ -28,23 +29,101 @@ func TestServerCreation(t *testing.T) {
2829
t.Errorf("expected prefix 'kn func', got '%s'", server.prefix)
2930
}
3031

31-
// Verify executor was set correctly
32-
if server.executor != mock {
32+
// Verify executor was set
33+
if server.executor != executor {
3334
t.Error("expected executor to be the mock")
3435
}
36+
}
37+
38+
// TestStart ensures the server starts and a client can retrieve server metadata
39+
func TestStart(t *testing.T) {
40+
var (
41+
ctx, cancel = context.WithCancel(context.Background())
42+
serverTpt, clientTpt = mcp.NewInMemoryTransports()
43+
client = mcp.NewClient(&mcp.Implementation{
44+
Name: "test-client",
45+
Version: "1.0.0",
46+
}, nil)
47+
)
48+
defer cancel()
49+
50+
// Create server with mock executor
51+
executor := mock.NewExecutor()
52+
server := New(WithExecutor(executor))
53+
54+
// Connect Server
55+
serverSession, err := server.Connect(ctx, serverTpt)
56+
if err != nil {
57+
t.Fatal(err)
58+
}
59+
defer serverSession.Close()
60+
61+
// Connect Client
62+
clientSession, err := client.Connect(ctx, clientTpt, nil)
63+
if err != nil {
64+
t.Fatal(err)
65+
}
66+
defer clientSession.Close()
67+
68+
// Verify initialization result contains server info
69+
initResult := clientSession.InitializeResult()
70+
if initResult == nil {
71+
t.Fatal("expected non-nil initialization result")
72+
}
73+
if initResult.ServerInfo.Name != "func-mcp" {
74+
t.Errorf("expected server name 'func-mcp', got %q", initResult.ServerInfo.Name)
75+
}
76+
if initResult.ServerInfo.Version != "1.0.0" {
77+
t.Errorf("expected server version '1.0.0', got %q", initResult.ServerInfo.Version)
78+
}
3579

36-
// Verify tools were registered
37-
if len(server.tools) != 9 {
38-
t.Errorf("expected 9 tools, got %d", len(server.tools))
80+
// List tools - should have all 9 registered tools
81+
toolsResult, err := clientSession.ListTools(ctx, &mcp.ListToolsParams{})
82+
if err != nil {
83+
t.Fatalf("failed to list tools: %v", err)
84+
}
85+
if len(toolsResult.Tools) != 9 {
86+
t.Errorf("expected 9 tools, got %d", len(toolsResult.Tools))
3987
}
4088

41-
// Verify resources were registered
42-
if len(server.resources) != 13 {
43-
t.Errorf("expected 13 resources, got %d", len(server.resources))
89+
// Verify expected tool names are present
90+
expectedTools := map[string]bool{
91+
"healthcheck": false,
92+
"create": false,
93+
"build": false,
94+
"deploy": false,
95+
"delete": false,
96+
"list": false,
97+
"config_volumes": false,
98+
"config_labels": false,
99+
"config_envs": false,
100+
}
101+
for _, tool := range toolsResult.Tools {
102+
if _, ok := expectedTools[tool.Name]; ok {
103+
expectedTools[tool.Name] = true
104+
}
105+
}
106+
for name, found := range expectedTools {
107+
if !found {
108+
t.Errorf("expected tool %q not found in tools list", name)
109+
}
44110
}
45111

46-
// Verify prompts were registered
47-
if len(server.prompts) != 3 {
48-
t.Errorf("expected 3 prompts, got %d", len(server.prompts))
112+
// List resources - should have all 13 registered resources
113+
resourcesResult, err := clientSession.ListResources(ctx, &mcp.ListResourcesParams{})
114+
if err != nil {
115+
t.Fatalf("failed to list resources: %v", err)
116+
}
117+
if len(resourcesResult.Resources) != 13 {
118+
t.Errorf("expected 13 resources, got %d", len(resourcesResult.Resources))
119+
}
120+
121+
// List prompts - should have all 3 registered prompts
122+
promptsResult, err := clientSession.ListPrompts(ctx, &mcp.ListPromptsParams{})
123+
if err != nil {
124+
t.Fatalf("failed to list prompts: %v", err)
125+
}
126+
if len(promptsResult.Prompts) != 3 {
127+
t.Errorf("expected 3 prompts, got %d", len(promptsResult.Prompts))
49128
}
50129
}

0 commit comments

Comments
 (0)