Skip to content

Commit cbb9b45

Browse files
authored
tool/mcp: add unified tool filter interface and deprecate legacy filters (#654)
Breaking changes - Add WithToolFilterFunc() option for unified tool.FilterFunc interface - Mark WithToolFilter(), NewIncludeFilter(), NewExcludeFilter() as deprecated - Implement filter priority logic: unified filter takes precedence over legacy filter - Update examples to use new unified filter interface - Update Chinese and English documentation to recommend unified interface
1 parent 0bdf5d2 commit cbb9b45

File tree

12 files changed

+288
-534
lines changed

12 files changed

+288
-534
lines changed

docs/mkdocs/en/tool.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -500,19 +500,22 @@ agent := llmagent.New("ai-assistant",
500500

501501
### MCP Tool Filters
502502

503-
MCP ToolSets support filtering tools at creation time:
503+
MCP ToolSets support filtering tools at creation time. It's recommended to use the unified `tool.FilterFunc` interface:
504504

505505
```go
506-
// Include filter: only allow specified tools.
507-
includeFilter := mcp.NewIncludeFilter("get_weather", "get_news", "calculator")
506+
import (
507+
"trpc.group/trpc-go/trpc-agent-go/tool"
508+
"trpc.group/trpc-go/trpc-agent-go/tool/mcp"
509+
)
508510

509-
// Exclude filter: exclude specified tools.
510-
excludeFilter := mcp.NewExcludeFilter("deprecated_tool", "slow_tool")
511+
// ✅ Recommended: Use the unified filter interface
512+
includeFilter := tool.NewIncludeToolNamesFilter("get_weather", "get_news", "calculator")
513+
excludeFilter := tool.NewExcludeToolNamesFilter("deprecated_tool", "slow_tool")
511514

512-
// Apply filter.
513-
combinedToolSet := mcp.NewMCPToolSet(
515+
// Apply filter
516+
toolSet := mcp.NewMCPToolSet(
514517
connectionConfig,
515-
mcp.WithToolFilter(includeFilter),
518+
mcp.WithToolFilterFunc(includeFilter),
516519
)
517520
```
518521

docs/mkdocs/zh/tool.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -496,19 +496,22 @@ agent := llmagent.New("ai-assistant",
496496

497497
### MCP 工具过滤器
498498

499-
MCP 工具集支持在创建时过滤工具:
499+
MCP 工具集支持在创建时过滤工具。推荐使用统一的 `tool.FilterFunc` 接口
500500

501501
```go
502-
// 包含过滤器:只使用指定工具
503-
includeFilter := mcp.NewIncludeFilter("get_weather", "get_news", "calculator")
502+
import (
503+
"trpc.group/trpc-go/trpc-agent-go/tool"
504+
"trpc.group/trpc-go/trpc-agent-go/tool/mcp"
505+
)
504506

505-
// 排除过滤器:排除指定工具
506-
excludeFilter := mcp.NewExcludeFilter("deprecated_tool", "slow_tool")
507+
// ✅ 推荐:使用统一的过滤接口
508+
includeFilter := tool.NewIncludeToolNamesFilter("get_weather", "get_news", "calculator")
509+
excludeFilter := tool.NewExcludeToolNamesFilter("deprecated_tool", "slow_tool")
507510

508511
// 应用过滤器
509-
combinedToolSet := mcp.NewMCPToolSet(
512+
toolSet := mcp.NewMCPToolSet(
510513
connectionConfig,
511-
mcp.WithToolFilter(includeFilter),
514+
mcp.WithToolFilterFunc(includeFilter),
512515
)
513516
```
514517

examples/mcptool/main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (c *multiTurnChat) run() error {
6969
if err := c.setup(ctx); err != nil {
7070
return fmt.Errorf("setup failed: %w", err)
7171
}
72-
72+
7373
// Ensure runner resources are cleaned up (trpc-agent-go >= v0.5.0)
7474
defer c.runner.Close()
7575

@@ -102,7 +102,7 @@ func (c *multiTurnChat) setup(ctx context.Context) error {
102102
Args: []string{"run", "./stdioserver/main.go"},
103103
Timeout: 10 * time.Second,
104104
},
105-
mcp.WithToolFilter(mcp.NewIncludeFilter("echo", "add")),
105+
mcp.WithToolFilterFunc(tool.NewIncludeToolNamesFilter("echo", "add")),
106106
)
107107
fmt.Println("STDIO MCP Toolset created successfully")
108108

@@ -113,7 +113,7 @@ func (c *multiTurnChat) setup(ctx context.Context) error {
113113
ServerURL: "http://localhost:3000/mcp", // Use ServerURL instead of URL
114114
Timeout: 10 * time.Second,
115115
},
116-
mcp.WithToolFilter(mcp.NewIncludeFilter("get_weather", "get_news")),
116+
mcp.WithToolFilterFunc(tool.NewIncludeToolNamesFilter("get_weather", "get_news")),
117117
mcp.WithMCPOptions(
118118
// WithSimpleRetry(3): Uses default settings with 3 retry attempts
119119
// - MaxRetries: 3 (range: 0-10)
@@ -140,7 +140,7 @@ func (c *multiTurnChat) setup(ctx context.Context) error {
140140
"User-Agent": "trpc-agent-go/1.0.0",
141141
},
142142
},
143-
mcp.WithToolFilter(mcp.NewIncludeFilter("sse_recipe", "sse_health_tip")),
143+
mcp.WithToolFilterFunc(tool.NewIncludeToolNamesFilter("sse_recipe", "sse_health_tip")),
144144
// Enable session reconnection for automatic recovery when server restarts (max 3 attempts)
145145
mcp.WithSessionReconnect(3),
146146
mcp.WithMCPOptions(

tool/mcp/config.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"fmt"
1414
"time"
1515

16+
"trpc.group/trpc-go/trpc-agent-go/tool"
1617
mcp "trpc.group/trpc-go/trpc-mcp-go"
1718
)
1819

@@ -87,7 +88,7 @@ type SessionReconnectConfig struct {
8788
// toolSetConfig holds internal configuration for ToolSet.
8889
type toolSetConfig struct {
8990
connectionConfig ConnectionConfig
90-
toolFilter ToolFilter
91+
toolFilterFunc tool.FilterFunc // Tool filter function.
9192
mcpOptions []mcp.ClientOption // MCP client options.
9293
sessionReconnectConfig *SessionReconnectConfig // Session reconnection configuration.
9394
name string // ToolSet name for identification and conflict resolution.
@@ -96,10 +97,20 @@ type toolSetConfig struct {
9697
// ToolSetOption is a function type for configuring ToolSet.
9798
type ToolSetOption func(*toolSetConfig)
9899

99-
// WithToolFilter configures tool filtering.
100-
func WithToolFilter(filter ToolFilter) ToolSetOption {
100+
// WithToolFilterFunc configures tool filtering.
101+
//
102+
// Example:
103+
//
104+
// import "trpc.group/trpc-go/trpc-agent-go/tool"
105+
//
106+
// // Include only specific tools
107+
// mcp.WithToolFilterFunc(tool.NewIncludeToolNamesFilter("echo", "add"))
108+
//
109+
// // Exclude specific tools
110+
// mcp.WithToolFilterFunc(tool.NewExcludeToolNamesFilter("deprecated_tool"))
111+
func WithToolFilterFunc(filterFunc tool.FilterFunc) ToolSetOption {
101112
return func(c *toolSetConfig) {
102-
c.toolFilter = filter
113+
c.toolFilterFunc = filterFunc
103114
}
104115
}
105116

tool/mcp/config_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package mcp
1212
import (
1313
"testing"
1414

15+
"trpc.group/trpc-go/trpc-agent-go/tool"
1516
mcp "trpc.group/trpc-go/trpc-mcp-go"
1617
)
1718

@@ -49,15 +50,15 @@ func TestValidateTransport(t *testing.T) {
4950
}
5051
}
5152

52-
func TestWithToolFilter(t *testing.T) {
53-
filter := NewIncludeFilter("tool1", "tool2")
53+
func TestWithToolFilterFunc(t *testing.T) {
54+
filter := tool.NewIncludeToolNamesFilter("tool1", "tool2")
5455
cfg := &toolSetConfig{}
5556

56-
opt := WithToolFilter(filter)
57+
opt := WithToolFilterFunc(filter)
5758
opt(cfg)
5859

59-
if cfg.toolFilter == nil {
60-
t.Error("expected toolFilter to be set")
60+
if cfg.toolFilterFunc == nil {
61+
t.Error("expected toolFilterFunc to be set")
6162
}
6263
}
6364

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//
2+
// Tencent is pleased to support the open source community by making trpc-agent-go available.
3+
//
4+
// Copyright (C) 2025 Tencent. All rights reserved.
5+
//
6+
// trpc-agent-go is licensed under the Apache License Version 2.0.
7+
//
8+
//
9+
10+
package e2e
11+
12+
import (
13+
"context"
14+
"testing"
15+
"time"
16+
17+
"github.com/stretchr/testify/assert"
18+
"github.com/stretchr/testify/require"
19+
"trpc.group/trpc-go/trpc-agent-go/tool"
20+
"trpc.group/trpc-go/trpc-agent-go/tool/mcp"
21+
)
22+
23+
// TestFilterPriority_Integration tests the filter logic using a real STDIO MCP server.
24+
func TestFilterPriority_Integration(t *testing.T) {
25+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
26+
defer cancel()
27+
28+
tests := []struct {
29+
name string
30+
setupConfig func() mcp.ConnectionConfig
31+
options []mcp.ToolSetOption
32+
expectedTools []string
33+
description string
34+
}{
35+
{
36+
name: "include filter",
37+
setupConfig: func() mcp.ConnectionConfig {
38+
return mcp.ConnectionConfig{
39+
Transport: "stdio",
40+
Command: "go",
41+
Args: []string{"run", "./test_server/main.go"},
42+
Timeout: 10 * time.Second,
43+
}
44+
},
45+
options: []mcp.ToolSetOption{
46+
mcp.WithToolFilterFunc(tool.NewIncludeToolNamesFilter("tool1")),
47+
},
48+
expectedTools: []string{"tool1"},
49+
description: "Include filter should only return tool1",
50+
},
51+
{
52+
name: "exclude filter",
53+
setupConfig: func() mcp.ConnectionConfig {
54+
return mcp.ConnectionConfig{
55+
Transport: "stdio",
56+
Command: "go",
57+
Args: []string{"run", "./test_server/main.go"},
58+
Timeout: 10 * time.Second,
59+
}
60+
},
61+
options: []mcp.ToolSetOption{
62+
mcp.WithToolFilterFunc(tool.NewExcludeToolNamesFilter("tool2")),
63+
},
64+
expectedTools: []string{"tool1", "tool3"},
65+
description: "Exclude filter should work correctly",
66+
},
67+
}
68+
69+
for _, tt := range tests {
70+
t.Run(tt.name, func(t *testing.T) {
71+
// Create toolset with test configuration
72+
config := tt.setupConfig()
73+
ts := mcp.NewMCPToolSet(config, tt.options...)
74+
defer ts.Close()
75+
76+
// Call Tools() - this executes the filter priority logic
77+
tools := ts.Tools(ctx)
78+
79+
// Verify filtered results
80+
require.Len(t, tools, len(tt.expectedTools), tt.description)
81+
82+
actualNames := make([]string, len(tools))
83+
for i, tool := range tools {
84+
actualNames[i] = tool.Declaration().Name
85+
}
86+
87+
assert.ElementsMatch(t, tt.expectedTools, actualNames, tt.description)
88+
89+
t.Logf("✓ %s - got tools: %v", tt.description, actualNames)
90+
})
91+
}
92+
}

tool/mcp/e2e/test_server/main.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// Tencent is pleased to support the open source community by making trpc-agent-go available.
3+
//
4+
// Copyright (C) 2025 Tencent. All rights reserved.
5+
//
6+
// trpc-agent-go is licensed under the Apache License Version 2.0.
7+
//
8+
//
9+
10+
// Simple STDIO server for integration testing.
11+
package main
12+
13+
import (
14+
"context"
15+
"log"
16+
17+
mcp "trpc.group/trpc-go/trpc-mcp-go"
18+
)
19+
20+
func main() {
21+
// Create STDIO server with simple tools for testing.
22+
server := mcp.NewStdioServer("filter-test-server", "1.0.0")
23+
24+
// Register 3 tools for filter testing
25+
server.RegisterTool(
26+
mcp.NewTool("tool1", mcp.WithDescription("First test tool")),
27+
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
28+
return mcp.NewTextResult("tool1 executed"), nil
29+
},
30+
)
31+
32+
server.RegisterTool(
33+
mcp.NewTool("tool2", mcp.WithDescription("Second test tool")),
34+
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
35+
return mcp.NewTextResult("tool2 executed"), nil
36+
},
37+
)
38+
39+
server.RegisterTool(
40+
mcp.NewTool("tool3", mcp.WithDescription("Third test tool")),
41+
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
42+
return mcp.NewTextResult("tool3 executed"), nil
43+
},
44+
)
45+
46+
log.Printf("Starting filter integration test STDIO server...")
47+
if err := server.Start(); err != nil {
48+
log.Fatalf("Server failed: %v", err)
49+
}
50+
}

0 commit comments

Comments
 (0)