-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathmcp_filter_test.go
More file actions
100 lines (84 loc) · 2.74 KB
/
Copy pathmcp_filter_test.go
File metadata and controls
100 lines (84 loc) · 2.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package cogito
import (
"context"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
mcpsdk "github.com/modelcontextprotocol/go-sdk/mcp"
)
// startInMemoryMCP spins up an MCP server with the named no-op tools,
// connects an in-memory client, and returns the connected session
// along with a teardown function.
func startInMemoryMCP(toolNames ...string) (*mcpsdk.ClientSession, func()) {
impl := &mcpsdk.Implementation{Name: "stub", Version: "0.0.1"}
srv := mcpsdk.NewServer(impl, nil)
for _, name := range toolNames {
name := name
mcpsdk.AddTool(
srv,
&mcpsdk.Tool{Name: name, Description: name + " (stub)"},
func(_ context.Context, _ *mcpsdk.CallToolRequest, _ map[string]any) (*mcpsdk.CallToolResult, map[string]any, error) {
return &mcpsdk.CallToolResult{}, nil, nil
},
)
}
srvT, clientT := mcpsdk.NewInMemoryTransports()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
go func() {
_ = srv.Run(ctx, srvT)
}()
client := mcpsdk.NewClient(impl, nil)
sess, err := client.Connect(ctx, clientT, nil)
Expect(err).ToNot(HaveOccurred())
teardown := func() {
_ = sess.Close()
cancel()
}
return sess, teardown
}
var _ = Describe("MCPToolFilter", func() {
var (
sess *mcpsdk.ClientSession
teardown func()
)
AfterEach(func() {
if teardown != nil {
teardown()
teardown = nil
}
})
It("drops tools the filter rejects from the discovered set", func() {
sess, teardown = startInMemoryMCP("list_issues", "delete_issue")
keep := map[string]bool{"list_issues": true} // delete_issue absent → drop
called := map[string]int{}
filter := func(s *mcpsdk.ClientSession, tool string) bool {
Expect(s).To(Equal(sess), "filter must receive the session it discovers from")
called[tool]++
return keep[tool]
}
tools, err := mcpToolsFromTransport(context.Background(), sess, filter)
Expect(err).ToNot(HaveOccurred())
Expect(tools).To(HaveLen(1))
mt, ok := tools[0].(*mcpTool)
Expect(ok).To(BeTrue())
Expect(mt.name).To(Equal("list_issues"))
Expect(called["list_issues"]).To(BeNumerically(">", 0))
Expect(called["delete_issue"]).To(BeNumerically(">", 0))
})
It("treats a nil filter as always-allow (default Options state)", func() {
sess, teardown = startInMemoryMCP("alpha", "beta")
tools, err := mcpToolsFromTransport(context.Background(), sess, nil)
Expect(err).ToNot(HaveOccurred())
Expect(tools).To(HaveLen(2))
})
It("yields an empty slice (not nil-error) when every tool is rejected", func() {
sess, teardown = startInMemoryMCP("x", "y", "z")
tools, err := mcpToolsFromTransport(
context.Background(),
sess,
func(*mcpsdk.ClientSession, string) bool { return false },
)
Expect(err).ToNot(HaveOccurred())
Expect(tools).To(BeEmpty())
})
})