Skip to main content

Scanning Details

Advanced configuration and operation of MCP Discovery scanning.

Scan Modes

Single Scan

Perform one scan and exit:

wl-discover --config config.toml scan

Useful for:

  • Testing configuration
  • CI/CD pipelines
  • Manual inventory updates

Continuous Daemon

Run as a background daemon:

wl-discover --config config.toml run

Features:

  • Periodic scanning at configured intervals
  • Health monitoring
  • Automatic registration updates
  • Graceful shutdown on SIGTERM

Dry Run

See what would be discovered without registering:

wl-discover --config config.toml scan --dry-run

Output:

[DRY RUN] Would register:
- web-search (new)
- filesystem (update: capabilities changed)
- slack (no change)

[DRY RUN] Would remove (stale):
- old-server (not found in scan)

Discovery Strategies

Filesystem Strategy

Scans directories for MCP configuration files:

[filesystem]
paths = [
"~/.config/mcp",
"/etc/mcp/servers.d",
"${MCP_CONFIG_DIR}"
]

# Glob patterns for config files
patterns = [
"*.json",
"*.toml",
"mcp-*.yaml",
"server.config"
]

# Recurse into subdirectories
recursive = true
max_depth = 3

# Follow symlinks
follow_symlinks = true

Configuration File Formats

JSON format:

{
"name": "my-server",
"command": "node",
"args": ["./server.js", "--port", "3000"],
"env": {
"API_KEY": "${MY_API_KEY}",
"DEBUG": "true"
},
"capabilities": {
"tools": ["tool1", "tool2"],
"resources": ["resource1"]
}
}

TOML format:

name = "my-server"
command = "python"
args = ["-m", "my_mcp_server"]

[env]
API_KEY = "${MY_API_KEY}"

[capabilities]
tools = ["analyze", "summarize"]

Claude Config Strategy

Reads from Claude Desktop/Code configuration:

[claude_config]
enabled = true

# Platform-specific paths
paths = [
# macOS
"~/Library/Application Support/Claude/claude_desktop_config.json",
# Linux
"~/.config/claude/claude_desktop_config.json",
# Windows (via WSL)
"/mnt/c/Users/${USER}/AppData/Roaming/Claude/claude_desktop_config.json"
]

Claude config structure:

{
"mcpServers": {
"server-name": {
"command": "command",
"args": ["arg1", "arg2"],
"env": {
"KEY": "value"
}
}
}
}

Network Strategy

Scans network for MCP servers:

[network]
enabled = true

# CIDR ranges to scan
ranges = [
"192.168.1.0/24",
"10.0.0.0/16"
]

# Ports to check
ports = [3000, 8080, 9000]

# Connection timeout
timeout_ms = 3000

# Concurrent connections
concurrency = 50

# Skip localhost
exclude_localhost = true

Process Strategy

Discovers running MCP server processes:

[process]
enabled = true

# Process name patterns
patterns = [
"mcp-server-*",
"node.*mcp",
"python.*mcp_server"
]

# Extract port from process arguments
extract_ports = true

Server Verification

After discovery, servers are verified:

[verification]
enabled = true
timeout_ms = 5000

# Verify by starting the server
start_verify = true

# Or verify by connection (for network servers)
connection_verify = true

# Extract capabilities by introspection
introspect_capabilities = true

Capability Introspection

The scanner can query servers for their capabilities:

[introspection]
enabled = true

# MCP introspection methods
methods = ["tools/list", "resources/list", "prompts/list"]

# Timeout for introspection
timeout_ms = 10000

Registration Behavior

New Servers

When a new server is discovered:

  1. Validate configuration
  2. Verify server can start (if enabled)
  3. Extract/introspect capabilities
  4. Register with registry
[registration]
# Automatically register new servers
auto_register = true

# Require manual approval for new servers
require_approval = false

# Default status for new servers
default_status = "active"

Updated Servers

When an existing server configuration changes:

[registration]
# Automatically update changed servers
auto_update = true

# Fields that trigger an update
update_on_change = [
"command",
"args",
"env",
"capabilities"
]

Stale Servers

When a server is no longer discovered:

[registration]
# Behavior for servers not found in scan
# Options: "mark_inactive", "delete", "ignore"
stale_action = "mark_inactive"

# Grace period before marking stale (scans)
stale_after_scans = 3

Filtering

Include Filters

Only register servers matching criteria:

[filters.include]
# Server name patterns
names = ["web-*", "api-*"]

# Required capabilities
capabilities = ["tools"]

# Minimum capability count
min_tools = 1

Exclude Filters

Skip servers matching criteria:

[filters.exclude]
# Server name patterns to skip
names = ["test-*", "dev-*"]

# Skip servers with these commands
commands = ["echo", "cat"]

# Skip by path
paths = ["*/test/*", "*/tmp/*"]

Scheduling

Scan Intervals

[scheduler]
# Main scan interval (seconds)
scan_interval = 300

# Health check interval (seconds)
health_interval = 60

# Stagger scans to avoid thundering herd
jitter_percent = 10

Maintenance Windows

[scheduler.maintenance]
# Skip scans during maintenance
enabled = true

# Maintenance window (UTC)
start_hour = 2
end_hour = 4
days = ["saturday", "sunday"]

Monitoring

Metrics

The scanner exposes Prometheus metrics:

[metrics]
enabled = true
port = 9090
path = "/metrics"

Available metrics:

  • mcp_discovery_scans_total
  • mcp_discovery_servers_found
  • mcp_discovery_servers_registered
  • mcp_discovery_errors_total
  • mcp_discovery_scan_duration_seconds

Alerting

[alerting]
enabled = true

# Alert on scan failures
on_scan_failure = true

# Alert when servers become unhealthy
on_unhealthy = true

# Webhook for alerts
webhook_url = "https://hooks.example.com/mcp-alerts"

CLI Reference

# Show help
mcp-discover --help

# Scan with verbose output
mcp-discover --config config.toml scan -v

# Dry run
mcp-discover --config config.toml scan --dry-run

# Run daemon with custom interval
mcp-discover --config config.toml run --interval 60

# Force re-registration of all servers
mcp-discover --config config.toml scan --force

# Export discovered servers to file
mcp-discover --config config.toml scan --export servers.json

Next Steps