Skip to content

Commit 8290cd9

Browse files
committed
Enhance MCP proxy server with debug search functionality and update configuration structure. Add debug search tool for relevancy analysis and improve indexing with combined searchable text. Update server initialization to support new debug search option.
1 parent b65e961 commit 8290cd9

6 files changed

Lines changed: 783 additions & 58 deletions

File tree

cmd/mcpproxy/main.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ import (
1717
)
1818

1919
var (
20-
configFile string
21-
dataDir string
22-
listen string
23-
logLevel string
24-
enableTray bool
25-
version = "v0.1.0" // This will be injected by -ldflags during build
20+
configFile string
21+
dataDir string
22+
listen string
23+
logLevel string
24+
enableTray bool
25+
debugSearch bool
26+
version = "v0.1.0" // This will be injected by -ldflags during build
2627
)
2728

2829
func main() {
@@ -39,6 +40,7 @@ func main() {
3940
rootCmd.PersistentFlags().StringVarP(&listen, "listen", "l", "", "Listen address (for HTTP mode, not used in stdio mode)")
4041
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "Log level (debug, info, warn, error)")
4142
rootCmd.PersistentFlags().BoolVar(&enableTray, "tray", true, "Enable system tray")
43+
rootCmd.PersistentFlags().BoolVar(&debugSearch, "debug-search", false, "Enable debug search tool for search relevancy debugging")
4244

4345
if err := rootCmd.Execute(); err != nil {
4446
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -68,6 +70,9 @@ func runServer(cmd *cobra.Command, args []string) error {
6870
// Override tray setting from command line
6971
cfg.EnableTray = enableTray
7072

73+
// Override debug search setting from command line
74+
cfg.DebugSearch = debugSearch
75+
7176
logger.Info("Configuration loaded",
7277
zap.String("data_dir", cfg.DataDir),
7378
zap.Int("servers_count", len(cfg.Servers)),

internal/config/config.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import (
77

88
// Config represents the main configuration structure
99
type Config struct {
10-
Listen string `json:"listen" mapstructure:"listen"`
11-
DataDir string `json:"data_dir" mapstructure:"data-dir"`
12-
EnableTray bool `json:"enable_tray" mapstructure:"tray"`
13-
Servers []*ServerConfig `json:"mcpServers" mapstructure:"servers"`
14-
TopK int `json:"top_k" mapstructure:"top-k"`
15-
ToolsLimit int `json:"tools_limit" mapstructure:"tools-limit"`
10+
Listen string `json:"listen" mapstructure:"listen"`
11+
DataDir string `json:"data_dir" mapstructure:"data-dir"`
12+
EnableTray bool `json:"enable_tray" mapstructure:"tray"`
13+
DebugSearch bool `json:"debug_search" mapstructure:"debug-search"`
14+
Servers []*ServerConfig `json:"mcpServers" mapstructure:"servers"`
15+
TopK int `json:"top_k" mapstructure:"top-k"`
16+
ToolsLimit int `json:"tools_limit" mapstructure:"tools-limit"`
1617
}
1718

1819
// ServerConfig represents upstream MCP server configuration
@@ -112,12 +113,13 @@ type ToolStatEntry struct {
112113
// DefaultConfig returns a default configuration
113114
func DefaultConfig() *Config {
114115
return &Config{
115-
Listen: ":8080",
116-
DataDir: "", // Will be set to ~/.mcpproxy by loader
117-
EnableTray: true,
118-
Servers: []*ServerConfig{},
119-
TopK: 5,
120-
ToolsLimit: 15,
116+
Listen: ":8080",
117+
DataDir: "", // Will be set to ~/.mcpproxy by loader
118+
EnableTray: true,
119+
DebugSearch: false,
120+
Servers: []*ServerConfig{},
121+
TopK: 5,
122+
ToolsLimit: 15,
121123
}
122124
}
123125

internal/index/bleve.go

Lines changed: 121 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package index
33
import (
44
"fmt"
55
"path/filepath"
6+
"strings"
67

78
"github.com/blevesearch/bleve/v2"
89
"github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
@@ -20,12 +21,14 @@ type BleveIndex struct {
2021

2122
// ToolDocument represents a tool document in the index
2223
type ToolDocument struct {
23-
ToolName string `json:"tool_name"`
24-
ServerName string `json:"server_name"`
25-
Description string `json:"description"`
26-
ParamsJSON string `json:"params_json"`
27-
Hash string `json:"hash"`
28-
Tags string `json:"tags"`
24+
ToolName string `json:"tool_name"` // Just the tool name (without server prefix)
25+
FullToolName string `json:"full_tool_name"` // Complete server:tool format
26+
ServerName string `json:"server_name"`
27+
Description string `json:"description"`
28+
ParamsJSON string `json:"params_json"`
29+
Hash string `json:"hash"`
30+
Tags string `json:"tags"`
31+
SearchableText string `json:"searchable_text"` // Combined searchable content
2932
}
3033

3134
// NewBleveIndex creates a new Bleve index
@@ -59,12 +62,19 @@ func createBleveIndex(indexPath string) (bleve.Index, error) {
5962
// Create document mapping for tools
6063
toolMapping := bleve.NewDocumentMapping()
6164

62-
// Tool name field (keyword analyzer - exact match)
63-
toolNameField := bleve.NewTextFieldMapping()
64-
toolNameField.Analyzer = keyword.Name
65-
toolNameField.Store = true
66-
toolNameField.Index = true
67-
toolMapping.AddFieldMappingsAt("tool_name", toolNameField)
65+
// Tool name field (both keyword and standard analyzers for different search types)
66+
toolNameFieldKeyword := bleve.NewTextFieldMapping()
67+
toolNameFieldKeyword.Analyzer = keyword.Name
68+
toolNameFieldKeyword.Store = true
69+
toolNameFieldKeyword.Index = true
70+
toolMapping.AddFieldMappingsAt("tool_name", toolNameFieldKeyword)
71+
72+
// Full tool name field (keyword analyzer for exact matches)
73+
fullToolNameField := bleve.NewTextFieldMapping()
74+
fullToolNameField.Analyzer = keyword.Name
75+
fullToolNameField.Store = true
76+
fullToolNameField.Index = true
77+
toolMapping.AddFieldMappingsAt("full_tool_name", fullToolNameField)
6878

6979
// Server name field (keyword analyzer)
7080
serverNameField := bleve.NewTextFieldMapping()
@@ -101,6 +111,13 @@ func createBleveIndex(indexPath string) (bleve.Index, error) {
101111
tagsField.Index = true
102112
toolMapping.AddFieldMappingsAt("tags", tagsField)
103113

114+
// Searchable text field (standard analyzer) - combines all searchable content
115+
searchableTextField := bleve.NewTextFieldMapping()
116+
searchableTextField.Analyzer = standard.Name
117+
searchableTextField.Store = false // Don't store, just index for search
118+
searchableTextField.Index = true
119+
toolMapping.AddFieldMappingsAt("searchable_text", searchableTextField)
120+
104121
// Add document mapping to index
105122
indexMapping.AddDocumentMapping("tool", toolMapping)
106123
indexMapping.DefaultMapping = toolMapping
@@ -116,19 +133,34 @@ func (b *BleveIndex) Close() error {
116133

117134
// IndexTool indexes a tool document
118135
func (b *BleveIndex) IndexTool(toolMeta *config.ToolMetadata) error {
136+
// Extract just the tool name (remove server prefix)
137+
toolName := toolMeta.Name
138+
if parts := strings.SplitN(toolMeta.Name, ":", 2); len(parts) == 2 {
139+
toolName = parts[1]
140+
}
141+
142+
// Create combined searchable text for better full-text search
143+
searchableText := fmt.Sprintf("%s %s %s %s",
144+
toolName,
145+
toolMeta.Name,
146+
toolMeta.Description,
147+
toolMeta.ParamsJSON)
148+
119149
doc := &ToolDocument{
120-
ToolName: toolMeta.Name,
121-
ServerName: toolMeta.ServerName,
122-
Description: toolMeta.Description,
123-
ParamsJSON: toolMeta.ParamsJSON,
124-
Hash: toolMeta.Hash,
125-
Tags: "", // Can be extended later
150+
ToolName: toolName,
151+
FullToolName: toolMeta.Name,
152+
ServerName: toolMeta.ServerName,
153+
Description: toolMeta.Description,
154+
ParamsJSON: toolMeta.ParamsJSON,
155+
Hash: toolMeta.Hash,
156+
Tags: "", // Can be extended later
157+
SearchableText: searchableText,
126158
}
127159

128160
// Use server:tool format as document ID for uniqueness
129-
docID := fmt.Sprintf("%s:%s", toolMeta.ServerName, toolMeta.Name)
161+
docID := fmt.Sprintf("%s:%s", toolMeta.ServerName, toolName)
130162

131-
b.logger.Debug("Indexing tool", zap.String("doc_id", docID))
163+
b.logger.Debug("Indexing tool", zap.String("doc_id", docID), zap.String("tool_name", toolName))
132164
return b.index.Index(docID, doc)
133165
}
134166

@@ -168,22 +200,59 @@ func (b *BleveIndex) DeleteServerTools(serverName string) error {
168200
return nil
169201
}
170202

171-
// SearchTools searches for tools using BM25 scoring
172-
func (b *BleveIndex) SearchTools(query string, limit int) ([]*config.SearchResult, error) {
173-
if query == "" {
203+
// SearchTools searches for tools using multiple query strategies for better results
204+
func (b *BleveIndex) SearchTools(queryStr string, limit int) ([]*config.SearchResult, error) {
205+
if queryStr == "" {
174206
return nil, fmt.Errorf("search query cannot be empty")
175207
}
176208

177-
// Create a match query for full-text search
178-
matchQuery := bleve.NewMatchQuery(query)
209+
// Create a boolean query to combine multiple search strategies
210+
boolQuery := bleve.NewBooleanQuery()
211+
212+
// 1. Exact match on tool name (highest priority)
213+
exactToolNameQuery := bleve.NewTermQuery(queryStr)
214+
exactToolNameQuery.SetField("tool_name")
215+
exactToolNameQuery.SetBoost(5.0)
216+
boolQuery.AddShould(exactToolNameQuery)
217+
218+
// 2. Exact match on full tool name
219+
exactFullToolNameQuery := bleve.NewTermQuery(queryStr)
220+
exactFullToolNameQuery.SetField("full_tool_name")
221+
exactFullToolNameQuery.SetBoost(4.0)
222+
boolQuery.AddShould(exactFullToolNameQuery)
223+
224+
// 3. Prefix match on tool name for partial matches
225+
prefixToolNameQuery := bleve.NewPrefixQuery(queryStr)
226+
prefixToolNameQuery.SetField("tool_name")
227+
prefixToolNameQuery.SetBoost(3.0)
228+
boolQuery.AddShould(prefixToolNameQuery)
229+
230+
// 4. Wildcard search for underscore-separated terms
231+
if strings.Contains(queryStr, "_") {
232+
wildcardQuery := bleve.NewWildcardQuery("*" + queryStr + "*")
233+
wildcardQuery.SetField("tool_name")
234+
wildcardQuery.SetBoost(2.5)
235+
boolQuery.AddShould(wildcardQuery)
236+
}
237+
238+
// 5. Full-text search across all fields
239+
matchQuery := bleve.NewMatchQuery(queryStr)
240+
matchQuery.SetBoost(1.0)
241+
boolQuery.AddShould(matchQuery)
242+
243+
// 6. Search in combined searchable text
244+
searchableTextQuery := bleve.NewMatchQuery(queryStr)
245+
searchableTextQuery.SetField("searchable_text")
246+
searchableTextQuery.SetBoost(1.5)
247+
boolQuery.AddShould(searchableTextQuery)
179248

180249
// Create search request
181-
searchReq := bleve.NewSearchRequest(matchQuery)
250+
searchReq := bleve.NewSearchRequest(boolQuery)
182251
searchReq.Size = limit
183-
searchReq.Fields = []string{"tool_name", "server_name", "description", "params_json", "hash"}
252+
searchReq.Fields = []string{"tool_name", "full_tool_name", "server_name", "description", "params_json", "hash"}
184253
searchReq.Highlight = bleve.NewHighlight()
185254

186-
b.logger.Debug("Searching tools", zap.String("query", query), zap.Int("limit", limit))
255+
b.logger.Debug("Searching tools with enhanced query", zap.String("query", queryStr), zap.Int("limit", limit))
187256

188257
searchResult, err := b.index.Search(searchReq)
189258
if err != nil {
@@ -194,7 +263,7 @@ func (b *BleveIndex) SearchTools(query string, limit int) ([]*config.SearchResul
194263
var results []*config.SearchResult
195264
for _, hit := range searchResult.Hits {
196265
toolMeta := &config.ToolMetadata{
197-
Name: getStringField(hit.Fields, "tool_name"),
266+
Name: getStringField(hit.Fields, "full_tool_name"),
198267
ServerName: getStringField(hit.Fields, "server_name"),
199268
Description: getStringField(hit.Fields, "description"),
200269
ParamsJSON: getStringField(hit.Fields, "params_json"),
@@ -207,7 +276,7 @@ func (b *BleveIndex) SearchTools(query string, limit int) ([]*config.SearchResul
207276
})
208277
}
209278

210-
b.logger.Debug("Found tools matching query", zap.Int("count", len(results)), zap.String("query", query))
279+
b.logger.Debug("Found tools matching query", zap.Int("count", len(results)), zap.String("query", queryStr))
211280
return results, nil
212281
}
213282

@@ -223,16 +292,31 @@ func (b *BleveIndex) BatchIndex(tools []*config.ToolMetadata) error {
223292
batch := b.index.NewBatch()
224293

225294
for _, toolMeta := range tools {
295+
// Extract just the tool name (remove server prefix)
296+
toolName := toolMeta.Name
297+
if parts := strings.SplitN(toolMeta.Name, ":", 2); len(parts) == 2 {
298+
toolName = parts[1]
299+
}
300+
301+
// Create combined searchable text
302+
searchableText := fmt.Sprintf("%s %s %s %s",
303+
toolName,
304+
toolMeta.Name,
305+
toolMeta.Description,
306+
toolMeta.ParamsJSON)
307+
226308
doc := &ToolDocument{
227-
ToolName: toolMeta.Name,
228-
ServerName: toolMeta.ServerName,
229-
Description: toolMeta.Description,
230-
ParamsJSON: toolMeta.ParamsJSON,
231-
Hash: toolMeta.Hash,
232-
Tags: "",
309+
ToolName: toolName,
310+
FullToolName: toolMeta.Name,
311+
ServerName: toolMeta.ServerName,
312+
Description: toolMeta.Description,
313+
ParamsJSON: toolMeta.ParamsJSON,
314+
Hash: toolMeta.Hash,
315+
Tags: "",
316+
SearchableText: searchableText,
233317
}
234318

235-
docID := fmt.Sprintf("%s:%s", toolMeta.ServerName, toolMeta.Name)
319+
docID := fmt.Sprintf("%s:%s", toolMeta.ServerName, toolName)
236320
batch.Index(docID, doc)
237321
}
238322

0 commit comments

Comments
 (0)