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
31 changes: 31 additions & 0 deletions cmd/mcpproxy/doctor_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ func outputDiagnostics(diag map[string]interface{}, info map[string]interface{})
fmt.Println("✅ All systems operational! No issues detected.")
fmt.Println()

// Show deprecated config warnings even when no issues
displayDeprecatedConfigs(diag)

// Display security features status even when no issues
fmt.Println("🔒 Security Features")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
Expand Down Expand Up @@ -317,6 +320,9 @@ func outputDiagnostics(diag map[string]interface{}, info map[string]interface{})
fmt.Println()
}

// Deprecated Configuration warnings
displayDeprecatedConfigs(diag)

fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println()
fmt.Println("For more details, run: mcpproxy doctor --output=json")
Expand Down Expand Up @@ -381,6 +387,31 @@ func sortArrayByServerName(arr []interface{}) {
})
}

// displayDeprecatedConfigs shows deprecated configuration warnings in the doctor output.
func displayDeprecatedConfigs(diag map[string]interface{}) {
deprecatedConfigs := getArrayField(diag, "deprecated_configs")
if len(deprecatedConfigs) == 0 {
return
}

fmt.Println("⚠️ Deprecated Configuration")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
for _, item := range deprecatedConfigs {
if cfgMap, ok := item.(map[string]interface{}); ok {
field := getStringField(cfgMap, "field")
message := getStringField(cfgMap, "message")
replacement := getStringField(cfgMap, "replacement")

fmt.Printf("\n • %s\n", field)
fmt.Printf(" %s\n", message)
if replacement != "" {
fmt.Printf(" Suggestion: %s\n", replacement)
}
}
}
fmt.Println()
}

// displaySecurityFeaturesStatus shows the status of security features in the doctor output.
func displaySecurityFeaturesStatus() {
// Load config to check security feature settings
Expand Down
10 changes: 1 addition & 9 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,20 @@ MCPProxy looks for configuration in these locations (in order):

```json
{
"enable_tray": true,
"enable_socket": true,
"tray_endpoint": ""
}
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `enable_tray` | boolean | `true` | Enable system tray application |
| `enable_socket` | boolean | `true` | Enable Unix socket (macOS/Linux) or named pipe (Windows) for secure local IPC between tray and core |
| `tray_endpoint` | string | `""` | Override socket/pipe path (advanced, usually not needed) |

### Search & Tool Limits

```json
{
"top_k": 5,
"tools_limit": 15,
"tool_response_limit": 20000,
"call_tool_timeout": "2m"
Expand All @@ -95,7 +92,6 @@ MCPProxy looks for configuration in these locations (in order):

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `top_k` | integer | `5` | Number of top search results to return (1-100) |
| `tools_limit` | integer | `15` | Maximum number of tools to return per request (1-1000) |
| `tool_response_limit` | integer | `20000` | Maximum characters in tool responses (0 = unlimited) |
| `call_tool_timeout` | string | `"2m"` | Timeout for tool calls (e.g., `"30s"`, `"2m"`, `"5m"`). **Note**: When using agents like Codex or Claude as MCP servers, you may need to increase this timeout significantly, even up to 10 minutes (`"10m"`), as these agents may require longer processing times for complex operations |
Expand Down Expand Up @@ -667,7 +663,6 @@ See [Code Execution Documentation](code_execution/overview.md) for complete deta
"enable_caching": true,
"enable_async_storage": true,
"enable_web_ui": true,
"enable_tray": true,
"enable_debug_logging": false,
"enable_contract_tests": false
}
Expand Down Expand Up @@ -728,10 +723,8 @@ Here's a complete configuration example with all major sections:
{
"listen": "127.0.0.1:8080",
"data_dir": "~/.mcpproxy",
"enable_tray": true,
"enable_socket": true,
"api_key": "",
"top_k": 5,
"tools_limit": 15,
"tool_response_limit": 20000,
"call_tool_timeout": "2m",
Expand Down Expand Up @@ -832,7 +825,7 @@ Many configuration options can be overridden via environment variables:
| `HEADLESS` | - | Run in headless mode |

**Prefix rules:**
- General settings also accept the `MCPP_` prefix (hyphens become underscores), e.g., `MCPP_TOP_K`, `MCPP_TOOLS_LIMIT`, `MCPP_ENABLE_PROMPTS`.
- General settings also accept the `MCPP_` prefix (hyphens become underscores), e.g., `MCPP_TOOLS_LIMIT`, `MCPP_ENABLE_PROMPTS`.
- TLS/listen/data have additional convenience overrides with the `MCPPROXY_` prefix as listed above.

**Priority:** Environment variables > Config file > Defaults
Expand All @@ -844,7 +837,6 @@ Many configuration options can be overridden via environment variables:
MCPProxy validates configuration on startup. Common validation errors:

- **Invalid listen address**: Must be `host:port` or `:port` format
- **Invalid top_k**: Must be between 1 and 100
- **Invalid tools_limit**: Must be between 1 and 1000
- **Missing server name**: Each server must have a unique name
- **Invalid protocol**: Must be `stdio`, `http`, `sse`, `streamable-http`, or `auto`
Expand Down
2 changes: 0 additions & 2 deletions docs/configuration/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ MCPProxy uses a JSON configuration file located at `~/.mcpproxy/mcp_config.json`
"data_dir": "~/.mcpproxy",
"api_key": "your-secret-api-key",
"enable_socket": true,
"top_k": 5,
"tools_limit": 15,
"tool_response_limit": 20000,
"enable_code_execution": false,
Expand Down Expand Up @@ -62,7 +61,6 @@ MCPProxy uses a JSON configuration file located at `~/.mcpproxy/mcp_config.json`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `top_k` | integer | `5` | Number of top results for tool search |
| `tools_limit` | integer | `15` | Maximum tools to return in a single request |
| `tool_response_limit` | integer | `20000` | Maximum characters in tool response |

Expand Down
6 changes: 2 additions & 4 deletions docs/features/search-discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,12 @@ curl -H "X-API-Key: your-key" \

```json
{
"top_k": 5,
"tools_limit": 15
}
```

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `top_k` | integer | `5` | Default number of results |
| `tools_limit` | integer | `15` | Maximum results per query |

### Index Location
Expand Down Expand Up @@ -225,8 +223,8 @@ mcpproxy serve
AI clients typically use MCPProxy's search in two ways:

1. **Direct Tool Call**: AI calls `retrieve_tools` to find relevant tools
2. **Automatic Discovery**: MCPProxy returns top-K tools matching the task context
2. **Automatic Discovery**: MCPProxy returns matching tools based on the task context

The `top_k` setting controls how many tools are suggested to the AI, balancing between:
The `tools_limit` setting controls how many tools are suggested to the AI, balancing between:
- More tools = better coverage but higher token usage
- Fewer tools = faster responses but may miss relevant tools
1 change: 0 additions & 1 deletion docs/repository-detection.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ Repository detection is enabled by default in new configurations:
{
"listen": ":8080",
"data_dir": "",
"enable_tray": true,
"debug_search": false,
"check_server_repo": true,
"mcpServers": [],
Expand Down
3 changes: 0 additions & 3 deletions docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,8 @@ MCPProxy looks for configuration in these locations (in order):
{
"listen": ":8080",
"data_dir": "~/.mcpproxy",
"enable_tray": true,

// Search & tool limits
"top_k": 5,
"tools_limit": 15,
"tool_response_limit": 20000,

Expand Down Expand Up @@ -748,7 +746,6 @@ tail -f ~/Library/Logs/mcpproxy/main.log | grep -E "(github-server|oauth|error)"

```json
{
"top_k": 10, // More search results
"tools_limit": 25, // More tools per request
"tool_response_limit": 50000 // Larger response limit
}
Expand Down
31 changes: 5 additions & 26 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ type Config struct {
TrayEndpoint string `json:"tray_endpoint,omitempty" mapstructure:"tray-endpoint"` // Tray endpoint override (unix:// or npipe://)
EnableSocket bool `json:"enable_socket" mapstructure:"enable-socket"` // Enable Unix socket/named pipe for local IPC (default: true)
DataDir string `json:"data_dir" mapstructure:"data-dir"`
EnableTray bool `json:"enable_tray" mapstructure:"tray"`
// Deprecated: EnableTray is unused and has no runtime effect. Kept for backward compatibility.
EnableTray bool `json:"enable_tray,omitempty" mapstructure:"tray"`
DebugSearch bool `json:"debug_search" mapstructure:"debug-search"`
Servers []*ServerConfig `json:"mcpServers" mapstructure:"servers"`
TopK int `json:"top_k" mapstructure:"top-k"`
// Deprecated: TopK is superseded by ToolsLimit and has no runtime effect. Kept for backward compatibility.
TopK int `json:"top_k,omitempty" mapstructure:"top-k"`
ToolsLimit int `json:"tools_limit" mapstructure:"tools-limit"`
ToolResponseLimit int `json:"tool_response_limit" mapstructure:"tool-response-limit"`
CallToolTimeout Duration `json:"call_tool_timeout" mapstructure:"call-tool-timeout" swaggertype:"string"`
Expand Down Expand Up @@ -90,7 +92,7 @@ type Config struct {
// Registries configuration for MCP server discovery
Registries []RegistryEntry `json:"registries,omitempty" mapstructure:"registries"`

// Feature flags for modular functionality
// Deprecated: Features flags are unused and have no runtime effect. Kept for backward compatibility.
Features *FeatureFlags `json:"features,omitempty" mapstructure:"features"`

// TLS configuration
Expand Down Expand Up @@ -573,10 +575,8 @@ func DefaultConfig() *Config {
Listen: defaultPort,
EnableSocket: true, // Enable Unix socket/named pipe by default for local IPC
DataDir: "", // Will be set to ~/.mcpproxy by loader
EnableTray: true,
DebugSearch: false,
Servers: []*ServerConfig{},
TopK: 5,
ToolsLimit: 15,
ToolResponseLimit: 20000, // Default 20000 characters
CallToolTimeout: Duration(2 * time.Minute), // Default 2 minutes for tool calls
Expand Down Expand Up @@ -783,14 +783,6 @@ func (c *Config) ValidateDetailed() []ValidationError {
}
}

// Validate TopK range
if c.TopK < 1 || c.TopK > 100 {
errors = append(errors, ValidationError{
Field: "top_k",
Message: "must be between 1 and 100",
})
}

// Validate ToolsLimit range
if c.ToolsLimit < 1 || c.ToolsLimit > 1000 {
errors = append(errors, ValidationError{
Expand Down Expand Up @@ -951,9 +943,6 @@ func (c *Config) Validate() error {
if c.Listen == "" {
c.Listen = defaultPort
}
if c.TopK <= 0 {
c.TopK = 5
}
if c.ToolsLimit <= 0 {
c.ToolsLimit = 15
}
Expand Down Expand Up @@ -999,16 +988,6 @@ func (c *Config) Validate() error {
c.DockerIsolation = DefaultDockerIsolationConfig()
}

// Ensure Features config is not nil and validate dependencies
if c.Features == nil {
flags := DefaultFeatureFlags()
c.Features = &flags
} else {
if err := c.Features.ValidateFeatureFlags(); err != nil {
return fmt.Errorf("feature flag validation failed: %w", err)
}
}

// Ensure TLS config is not nil
if c.TLS == nil {
c.TLS = &TLSConfig{
Expand Down
24 changes: 2 additions & 22 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ func TestDefaultConfig(t *testing.T) {
// Test default values
assert.Equal(t, "127.0.0.1:8080", config.Listen)
assert.Equal(t, "", config.DataDir)
assert.True(t, config.EnableTray)
assert.False(t, config.DebugSearch)
assert.Equal(t, 5, config.TopK)
assert.Equal(t, 15, config.ToolsLimit)
assert.Equal(t, 20000, config.ToolResponseLimit)

Expand Down Expand Up @@ -49,19 +47,6 @@ func TestConfigValidation(t *testing.T) {
},
expected: &Config{
Listen: "127.0.0.1:8080",
TopK: 5,
ToolsLimit: 15,
ToolResponseLimit: 0,
},
},
{
name: "zero TopK defaults to 5",
config: &Config{
TopK: 0,
},
expected: &Config{
Listen: "127.0.0.1:8080",
TopK: 5,
ToolsLimit: 15,
ToolResponseLimit: 0,
},
Expand All @@ -73,7 +58,6 @@ func TestConfigValidation(t *testing.T) {
},
expected: &Config{
Listen: "127.0.0.1:8080",
TopK: 5,
ToolsLimit: 15,
ToolResponseLimit: 0,
},
Expand All @@ -85,7 +69,6 @@ func TestConfigValidation(t *testing.T) {
},
expected: &Config{
Listen: "127.0.0.1:8080",
TopK: 5,
ToolsLimit: 15,
ToolResponseLimit: 0,
},
Expand All @@ -97,7 +80,6 @@ func TestConfigValidation(t *testing.T) {
err := tt.config.Validate()
require.NoError(t, err)
assert.Equal(t, tt.expected.Listen, tt.config.Listen)
assert.Equal(t, tt.expected.TopK, tt.config.TopK)
assert.Equal(t, tt.expected.ToolsLimit, tt.config.ToolsLimit)
assert.Equal(t, tt.expected.ToolResponseLimit, tt.config.ToolResponseLimit)
})
Expand Down Expand Up @@ -436,8 +418,8 @@ func TestSaveAndLoadConfig(t *testing.T) {
t.Errorf("Expected Listen %s, got %s", cfg.Listen, loaded.Listen)
}

if loaded.TopK != cfg.TopK {
t.Errorf("Expected TopK %d, got %d", cfg.TopK, loaded.TopK)
if loaded.ToolsLimit != cfg.ToolsLimit {
t.Errorf("Expected ToolsLimit %d, got %d", cfg.ToolsLimit, loaded.ToolsLimit)
}
}

Expand Down Expand Up @@ -491,8 +473,6 @@ func TestLoadEmptyConfigFile(t *testing.T) {

// Verify the config still has default values
assert.Equal(t, "127.0.0.1:8080", cfg.Listen, "Default listen address should be preserved")
assert.True(t, cfg.EnableTray, "Default EnableTray should be preserved")
assert.Equal(t, 5, cfg.TopK, "Default TopK should be preserved")
})
}
}
Expand Down
54 changes: 54 additions & 0 deletions internal/config/deprecated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package config

import (
"encoding/json"
"os"
)

// DeprecatedField describes a deprecated configuration field.
type DeprecatedField struct {
JSONKey string `json:"json_key"`
Message string `json:"message"`
Replacement string `json:"replacement,omitempty"`
}

// deprecatedFields is the list of known deprecated config keys.
var deprecatedFields = []DeprecatedField{
{
JSONKey: "top_k",
Message: "top_k is deprecated and has no effect",
Replacement: "Use tools_limit instead",
},
{
JSONKey: "enable_tray",
Message: "enable_tray is deprecated and has no effect",
Replacement: "Remove from config (tray is managed by the tray application)",
},
{
JSONKey: "features",
Message: "features is deprecated and has no effect",
Replacement: "Remove from config (all feature flags are unused)",
},
}

// CheckDeprecatedFields reads the raw JSON config file and returns which
// deprecated keys are present. It does not validate or parse the full config.
func CheckDeprecatedFields(configPath string) []DeprecatedField {
data, err := os.ReadFile(configPath)
if err != nil {
return nil
}

var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil
}

var found []DeprecatedField
for _, df := range deprecatedFields {
if _, exists := raw[df.JSONKey]; exists {
found = append(found, df)
}
}
return found
}
Loading