Skip to content

Commit 2f77dd2

Browse files
committed
Add config
1 parent aff644c commit 2f77dd2

File tree

7 files changed

+480
-1
lines changed

7 files changed

+480
-1
lines changed

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,75 @@ Run the server:
2222
./stackrox-mcp
2323
```
2424

25+
## Configuration
26+
27+
The StackRox MCP server supports configuration through both YAML files and environment variables. Environment variables take precedence over YAML configuration.
28+
29+
### Configuration File
30+
31+
Specify a configuration file using the `--config` flag:
32+
33+
```bash
34+
./stackrox-mcp --config=/path/to/config.yaml
35+
```
36+
37+
See [examples/config-read-only.yaml](examples/config-read-only.yaml) for a complete configuration example.
38+
39+
### Environment Variables
40+
41+
All configuration options can be set via environment variables using the naming convention:
42+
43+
```
44+
STACKROX_MCP__SECTION__KEY
45+
```
46+
47+
Note the double underscore (`__`) separator between sections and keys.
48+
49+
#### Examples
50+
51+
```bash
52+
export STACKROX_MCP__CENTRAL__URL=central.stackrox:8443
53+
export STACKROX_MCP__GLOBAL__READ_ONLY_TOOLS=true
54+
export STACKROX_MCP__TOOLS__CONFIG_MANAGER__ENABLED=true
55+
```
56+
57+
### Configuration Options
58+
59+
#### Central Configuration
60+
61+
Configuration for connecting to StackRox Central.
62+
63+
| Option | Environment Variable | Type | Required | Default | Description |
64+
|--------|---------------------|------|----------|---------|-------------|
65+
| `central.url` | `STACKROX_MCP__CENTRAL__URL` | string | Yes | central.stackrox:8443 | URL of StackRox Central instance |
66+
| `central.insecure` | `STACKROX_MCP__CENTRAL__INSECURE` | bool | No | `false` | Skip TLS certificate verification |
67+
| `central.force_http1` | `STACKROX_MCP__CENTRAL__FORCE_HTTP1` | bool | No | `false` | Force HTTP/1.1 instead of HTTP/2 |
68+
69+
#### Global Configuration
70+
71+
Global MCP server settings.
72+
73+
| Option | Environment Variable | Type | Required | Default | Description |
74+
|--------|---------------------|------|----------|---------|-------------|
75+
| `global.read_only_tools` | `STACKROX_MCP__GLOBAL__READ_ONLY_TOOLS` | bool | No | `true` | Only allow read-only tools |
76+
77+
#### Tools Configuration
78+
79+
Enable or disable individual MCP tools. At least one tool has to be enabled.
80+
81+
| Option | Environment Variable | Type | Required | Default | Description |
82+
|--------|---------------------|------|----------|---------|-------------|
83+
| `tools.vulnerability.enabled` | `STACKROX_MCP__TOOLS__VULNERABILITY__ENABLED` | bool | No | `false` | Enable vulnerability management tools |
84+
| `tools.config_manager.enabled` | `STACKROX_MCP__TOOLS__CONFIG_MANAGER__ENABLED` | bool | No | `false` | Enable configuration management tools |
85+
86+
### Configuration Precedence
87+
88+
Configuration values are loaded in the following order (later sources override earlier ones):
89+
90+
1. Default values
91+
2. YAML configuration file (if provided via `--config`)
92+
3. Environment variables (highest precedence)
93+
2594
## Development
2695

2796
### Available Make Targets

cmd/stackrox-mcp/main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package main
33
import (
44
"github.com/rs/zerolog"
55
"github.com/rs/zerolog/log"
6+
flag "github.com/spf13/pflag"
7+
8+
"github.com/stackrox/stackrox-mcp/internal/config"
69
)
710

811
func setupLogging() {
@@ -12,5 +15,14 @@ func setupLogging() {
1215
func main() {
1316
setupLogging()
1417

18+
configPath := flag.String("config", "", "Path to configuration file (optional)")
19+
flag.Parse()
20+
21+
cfg, err := config.LoadConfig(*configPath)
22+
if err != nil {
23+
log.Fatal().Err(err).Msg("Failed to load configuration")
24+
}
25+
log.Info().Interface("config", cfg).Msg("Configuration loaded successfully")
26+
1527
log.Info().Msg("Starting Stackrox MCP server")
1628
}

examples/config-read-only.yaml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# StackRox MCP Server Configuration
2+
#
3+
# This is an example configuration file for the StackRox MCP server.
4+
# Copy this file and modify it according to your environment.
5+
#
6+
# Environment Variable Mapping:
7+
# All configuration options can be overridden using environment variables.
8+
# Environment variables take precedence over YAML configuration.
9+
#
10+
# Naming convention: STACKROX_MCP__SECTION__KEY
11+
# Example:
12+
# central:
13+
# url: central.stackrox:8443
14+
#
15+
# Can be overridden with:
16+
# STACKROX_MCP__CENTRAL__URL=central.stackrox:8443
17+
18+
# Central connection configuration
19+
central:
20+
# Central URL (required, default: central.stackrox:8443)
21+
# The URL of your StackRox Central instance
22+
url: central.stackrox:8443
23+
24+
# Allow insecure TLS connection (optional, default: false)
25+
# Set to true to skip TLS certificate verification
26+
insecure: false
27+
28+
# Force HTTP1 (optional, default: false)
29+
# Force HTTP/1.1 instead of HTTP/2
30+
force_http1: false
31+
32+
# Global MCP server configuration
33+
global:
34+
# Allow only read-only MCP tools (optional, default: true)
35+
# When true, only tools that perform read operations are available
36+
# When false, both read and write tools may be available (if implemented)
37+
read_only_tools: true
38+
39+
# Configuration of MCP tools
40+
# Each tool has an enable/disable flag. At least one tool has to be enabled.
41+
tools:
42+
# Vulnerability management tools
43+
vulnerability:
44+
# Enable vulnerability management tools (optional, default: false)
45+
enabled: true
46+
47+
# Configuration management tools
48+
config_manager:
49+
# Enable configuration management tools (optional, default: false)
50+
enabled: true

go.mod

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,21 @@ require (
99

1010
require (
1111
github.com/davecgh/go-spew v1.1.1 // indirect
12+
github.com/fsnotify/fsnotify v1.9.0 // indirect
13+
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
1214
github.com/mattn/go-colorable v0.1.13 // indirect
1315
github.com/mattn/go-isatty v0.0.19 // indirect
16+
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
1417
github.com/pmezard/go-difflib v1.0.0 // indirect
15-
golang.org/x/sys v0.12.0 // indirect
18+
github.com/sagikazarmark/locafero v0.11.0 // indirect
19+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
20+
github.com/spf13/afero v1.15.0 // indirect
21+
github.com/spf13/cast v1.10.0 // indirect
22+
github.com/spf13/pflag v1.0.10 // indirect
23+
github.com/spf13/viper v1.21.0 // indirect
24+
github.com/subosito/gotenv v1.6.0 // indirect
25+
go.yaml.in/yaml/v3 v3.0.4 // indirect
26+
golang.org/x/sys v0.29.0 // indirect
27+
golang.org/x/text v0.28.0 // indirect
1628
gopkg.in/yaml.v3 v3.0.1 // indirect
1729
)

go.sum

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,50 @@
11
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
22
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
33
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
5+
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
6+
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
7+
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
48
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
59
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
610
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
711
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
812
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
913
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
14+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
15+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
1016
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
1117
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1218
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1319
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
1420
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
1521
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
22+
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
23+
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
24+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
25+
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
26+
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
27+
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
28+
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
29+
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
30+
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
31+
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
32+
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
33+
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
1634
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
1735
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
36+
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
37+
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
38+
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
39+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
1840
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1941
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
2042
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
2143
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
44+
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
45+
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
46+
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
47+
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
2248
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2349
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2450
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

internal/config/config.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/spf13/viper"
8+
)
9+
10+
// Config represents the complete application configuration
11+
type Config struct {
12+
Central CentralConfig `mapstructure:"central"`
13+
Global GlobalConfig `mapstructure:"global"`
14+
Tools ToolsConfig `mapstructure:"tools"`
15+
}
16+
17+
// CentralConfig contains StackRox Central connection configuration
18+
type CentralConfig struct {
19+
URL string `mapstructure:"url"`
20+
Insecure bool `mapstructure:"insecure"`
21+
ForceHTTP1 bool `mapstructure:"force_http1"`
22+
}
23+
24+
// GlobalConfig contains global MCP server configuration
25+
type GlobalConfig struct {
26+
ReadOnlyTools bool `mapstructure:"read_only_tools"`
27+
}
28+
29+
// ToolsConfig contains configuration for individual MCP tools
30+
type ToolsConfig struct {
31+
Vulnerability VulnerabilityConfig `mapstructure:"vulnerability"`
32+
ConfigManager ConfigManagerConfig `mapstructure:"config_manager"`
33+
}
34+
35+
// VulnerabilityConfig contains configuration for vulnerability management tools
36+
type VulnerabilityConfig struct {
37+
Enabled bool `mapstructure:"enabled"`
38+
}
39+
40+
// ConfigManagerConfig contains configuration for config management tools
41+
type ConfigManagerConfig struct {
42+
Enabled bool `mapstructure:"enabled"`
43+
}
44+
45+
// LoadConfig loads configuration from YAML file and environment variables.
46+
// Environment variables take precedence over YAML configuration.
47+
// Env var naming convention: STACKROX_MCP__SECTION__KEY (double underscore as separator)
48+
// configPath: optional path to YAML configuration file (can be empty)
49+
func LoadConfig(configPath string) (*Config, error) {
50+
v := viper.New()
51+
52+
setDefaults(v)
53+
54+
// Set up environment variable support
55+
// Note: SetEnvPrefix adds a single underscore, so "STACKROX_MCP_" becomes the prefix
56+
// We want double underscores between sections, so we use "__" in the replacer
57+
v.SetEnvPrefix("STACKROX_MCP_")
58+
v.SetEnvKeyReplacer(strings.NewReplacer(".", "__"))
59+
v.AutomaticEnv()
60+
61+
if configPath != "" {
62+
v.SetConfigFile(configPath)
63+
if err := v.ReadInConfig(); err != nil {
64+
return nil, fmt.Errorf("failed to read config file: %w", err)
65+
}
66+
}
67+
68+
var cfg Config
69+
if err := v.Unmarshal(&cfg); err != nil {
70+
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
71+
}
72+
73+
if err := cfg.Validate(); err != nil {
74+
return nil, fmt.Errorf("invalid configuration: %w", err)
75+
}
76+
77+
return &cfg, nil
78+
}
79+
80+
// setDefaults sets default values for configuration
81+
func setDefaults(v *viper.Viper) {
82+
v.SetDefault("central.url", "central.stackrox:8443")
83+
v.SetDefault("central.insecure", false)
84+
v.SetDefault("central.force_http1", false)
85+
86+
v.SetDefault("global.read_only_tools", true)
87+
88+
v.SetDefault("tools.vulnerability.enabled", false)
89+
v.SetDefault("tools.config_manager.enabled", false)
90+
}
91+
92+
// Validate validates the configuration
93+
func (c *Config) Validate() error {
94+
if c.Central.URL == "" {
95+
return fmt.Errorf("central.url is required")
96+
}
97+
98+
if !c.Tools.Vulnerability.Enabled && !c.Tools.ConfigManager.Enabled {
99+
return fmt.Errorf("at least one tool has to be enabled")
100+
}
101+
102+
return nil
103+
}

0 commit comments

Comments
 (0)