Skip to content

Commit 0b02672

Browse files
committed
feat(config): Make the gatekeeper model more configurable
Move the gatekeeper configuration into a nested member off our config object, so LINUX_MCP_GATEKEEPER_MODEL becomes LINUX_MCP_GATEKEEPER__MODEL (LINUX_MCP_GATEKEEPER_MODEL is supported as a deprecated alias.) Add controls for: reasoning_effort: turn off or down reasoning often make models perform better for us. structured_output: e.g. for gemma-4-31b-it, turning off response_format is needed to keep the model from going into infinite looop. temperature: Anthropc models need a non-zero temperature to enable reasoning. quantization: OpenRouter mixes together models with different quantization in a single model name - specifying a specific quantization is needed for clean benchmarking data.
1 parent c74f0dc commit 0b02672

12 files changed

Lines changed: 274 additions & 83 deletions

File tree

.gitlab/ci/eval-gatekeeper.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ gatekeeper-eval-claude-4.6-opus:
5454
variables:
5555
MODEL_NAME: "claude-4.6-opus"
5656
script:
57-
- export LINUX_MCP_GATEKEEPER_MODEL="openrouter/anthropic/$MODEL_NAME"
57+
- export LINUX_MCP_GATEKEEPER__MODEL="openrouter/anthropic/$MODEL_NAME"
5858
- uv run eval/gatekeeper/run-eval.py --all -f json --output-all -o "$CI_PROJECT_DIR/data/$MODEL_NAME.json"
5959
rules:
6060
- if: $OPENROUTER_API_KEY

docs/config-reference.md

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,87 +3,93 @@
33
The Linux MCP Server is configured through command line options or environment variables. Environment variables use a `LINUX_MCP_` prefix. For example, `--log-level` corresponds to `LINUX_MCP_LOG_LEVEL`.
44

55
!!! note "Precedence"
6-
Command line options take precedence over environment variables. For MCP client configurations (Claude Desktop, Cursor, etc.), you typically use environment variables in the config file rather than command line arguments, but either will work.
6+
Command line options take precedence over environment variables. For MCP client configurations (Claude Desktop, Cursor, etc.), you typically use environment variables in the config file rather than command line arguments, but either will work.
77

88
To see available options, run `linux-mcp-server --help`.
99

1010
## Transport Settings
1111

12-
| Option / Env Var | Default | Description |
13-
|------------------|---------|-------------|
14-
| `--transport`<br>`LINUX_MCP_TRANSPORT` | `stdio` | Transport type: `stdio` or `http` |
15-
| `--host`<br>`LINUX_MCP_HOST` | `127.0.0.1` | Host address for HTTP transport |
16-
| `--port`<br>`LINUX_MCP_PORT` | `8000` | Port number for HTTP transport |
17-
| `--path`<br>`LINUX_MCP_PATH` | `/mcp` | Path for HTTP transport |
12+
| Option / Env Var | Default | Description |
13+
| -------------------------------------- | ----------- | --------------------------------- |
14+
| `--transport`<br>`LINUX_MCP_TRANSPORT` | `stdio` | Transport type: `stdio` or `http` |
15+
| `--host`<br>`LINUX_MCP_HOST` | `127.0.0.1` | Host address for HTTP transport |
16+
| `--port`<br>`LINUX_MCP_PORT` | `8000` | Port number for HTTP transport |
17+
| `--path`<br>`LINUX_MCP_PATH` | `/mcp` | Path for HTTP transport |
1818

1919
!!! warning "HTTP Transport Security"
20-
The HTTP transport does not currently have authentication. It should not be used in production or on untrusted networks.
20+
The HTTP transport does not currently have authentication. It should not be used in production or on untrusted networks.
2121

2222
!!! note
23-
Some clients, like Claude Desktop, require `stdio` transport.
23+
Some clients, like Claude Desktop, require `stdio` transport.
2424

2525
## SSH Connection Settings
2626

27-
| Option / Env Var | Default | Description |
28-
|------------------|---------|-------------|
29-
| `--user`<br>`LINUX_MCP_USER` | *(empty)* | Default username for SSH connections |
30-
| `--ssh-key-path`<br>`LINUX_MCP_SSH_KEY_PATH` | *(none)* | Path to SSH private key file |
31-
| `--key-passphrase`<br>`LINUX_MCP_KEY_PASSPHRASE` | *(empty)* | Passphrase for encrypted SSH key |
32-
| `--search-for-ssh-key`<br>`LINUX_MCP_SEARCH_FOR_SSH_KEY` | `False` | Auto-discover SSH keys in `~/.ssh` |
33-
| `--command-timeout`<br>`LINUX_MCP_COMMAND_TIMEOUT` | `30` | Local and remote command timeout in seconds |
27+
| Option / Env Var | Default | Description |
28+
| -------------------------------------------------------- | --------- | ------------------------------------------- |
29+
| `--user`<br>`LINUX_MCP_USER` | _(empty)_ | Default username for SSH connections |
30+
| `--ssh-key-path`<br>`LINUX_MCP_SSH_KEY_PATH` | _(none)_ | Path to SSH private key file |
31+
| `--key-passphrase`<br>`LINUX_MCP_KEY_PASSPHRASE` | _(empty)_ | Passphrase for encrypted SSH key |
32+
| `--search-for-ssh-key`<br>`LINUX_MCP_SEARCH_FOR_SSH_KEY` | `False` | Auto-discover SSH keys in `~/.ssh` |
33+
| `--command-timeout`<br>`LINUX_MCP_COMMAND_TIMEOUT` | `30` | Local and remote command timeout in seconds |
3434

3535
## SSH Security Settings
3636

37-
| Option / Env Var | Default | Description |
38-
|------------------|---------|-------------|
39-
| `--verify-host-keys` / `--no-verify-host-keys`<br>`LINUX_MCP_VERIFY_HOST_KEYS` | `True` | Verify remote host identity via known_hosts |
40-
| `--known-hosts-path`<br>`LINUX_MCP_KNOWN_HOSTS_PATH` | *(none)* | Custom path to known_hosts file |
37+
| Option / Env Var | Default | Description |
38+
| ------------------------------------------------------------------------------ | -------- | ------------------------------------------- |
39+
| `--verify-host-keys` / `--no-verify-host-keys`<br>`LINUX_MCP_VERIFY_HOST_KEYS` | `True` | Verify remote host identity via known_hosts |
40+
| `--known-hosts-path`<br>`LINUX_MCP_KNOWN_HOSTS_PATH` | _(none)_ | Custom path to known_hosts file |
4141

4242
See [SSH Configuration](ssh.md) for details on setting up SSH connections and managing host keys.
4343

4444
## Tool Settings
4545

46-
| Option / Env Var | Default | Description |
47-
|------------------|---------|-------------|
48-
| `--toolset`<br>`LINUX_MCP_TOOLSET` | `fixed` | Toolset: `fixed`, `run_script`, or `both` |
49-
| `--allowed-log-paths`<br>`LINUX_MCP_ALLOWED_LOG_PATHS` | *(none)* | Comma-separated allowlist of log file paths for `read_log_file` |
50-
| `--max-file-read-bytes`<br>`LINUX_MCP_MAX_FILE_READ_BYTES` | `1048576` | Maximum bytes `read_file` may return |
46+
| Option / Env Var | Default | Description |
47+
| ---------------------------------------------------------- | --------- | --------------------------------------------------------------- |
48+
| `--toolset`<br>`LINUX_MCP_TOOLSET` | `fixed` | Toolset: `fixed`, `run_script`, or `both` |
49+
| `--allowed-log-paths`<br>`LINUX_MCP_ALLOWED_LOG_PATHS` | _(none)_ | Comma-separated allowlist of log file paths for `read_log_file` |
50+
| `--max-file-read-bytes`<br>`LINUX_MCP_MAX_FILE_READ_BYTES` | `1048576` | Maximum bytes `read_file` may return |
5151

5252
See [Guarded Command Execution](guarded-command-execution.md) for details on the `run_script` toolset.
5353

5454
## Guarded Command Execution Settings
5555

5656
These are used when `LINUX_MCP_TOOLSET` is set to `run_script` or `both`.
5757

58-
| Option / Env Var | Default | Description |
59-
|------------------|---------|-------------|
60-
| `--gatekeeper-model`<br>`LINUX_MCP_GATEKEEPER_MODEL` | *(none)* | Required: [LiteLLM model name](https://docs.litellm.ai/docs/providers) to use |
61-
| `--always-confirm-scripts` / `--no-always-confirm-scripts`<br>`LINUX_MCP_ALWAYS_CONFIRM_SCRIPTS` | `False` | All scripts must be confirmed by the user |
62-
| Other environment variables | *(none)* | As required by the LiteLLM provider, e.g. `OPENAI_API_KEY` |
58+
| Option / Env Var | Default | Description |
59+
| ----------------------------------------------------------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
60+
| `--gatekeeper.model`<br>`LINUX_MCP_GATEKEEPER__MODEL` | _(none)_ | Required: [LiteLLM model name](https://docs.litellm.ai/docs/providers) to use |
61+
| `--gatekeeper.quantization`<br>`LINUX_MCP_GATEKEEPER__QUANTIZATION` | _(model specific)_ | _Not usually needed_ - Particular model quantization to use (openrouter only) |
62+
| `--gatekeeper.reasoning_effort`<br>`LINUX_MCP_GATEKEEPER__REASONING_EFFORT` | _(model specific)_ | Reasoning effort to use for gatekeeper model (`none`, `minimal`, `low`, `medium`, `high`, `xhigh`). Not all values are supported for all models. Special values (`enable_thinking`, `disable_thinking`) set the `enable_thinking` chat template parameter. |
63+
| `--gatekeeper.structured_output`<br>`LINUX_MCP_GATEKEEPER__STRUCTURED_OUTPUT` | _(autodected)_ | _Not usually needed_ - Whether to use structured output generation for the model. Default is to use if detected as available. |
64+
| `--gatekeeper.temperature`<br>`LINUX_MCP_GATEKEEPER__TEMPERATURE` | 0.0 | _Not usually needed_ - Temperature to use for model - for some models, a non-zero value may be necessary when enabling reasoning |
65+
| Other environment variables | _(none)_ | As required by the LiteLLM provider, e.g. `OPENAI_API_KEY` |
6366

6467
## Logging Configuration
6568

66-
| Option / Env Var | Default | Description |
67-
|------------------|---------|-------------|
68-
| `--log-dir`<br>`LINUX_MCP_LOG_DIR` | `~/.local/share/linux-mcp-server/logs` | Directory for server logs |
69-
| `--log-level`<br>`LINUX_MCP_LOG_LEVEL` | `INFO` | Log verbosity: `DEBUG`, `INFO`, `WARNING` |
70-
| `--log-retention-days`<br>`LINUX_MCP_LOG_RETENTION_DAYS` | `10` | Days to retain log files |
69+
| Option / Env Var | Default | Description |
70+
| -------------------------------------------------------- | -------------------------------------- | ----------------------------------------- |
71+
| `--log-dir`<br>`LINUX_MCP_LOG_DIR` | `~/.local/share/linux-mcp-server/logs` | Directory for server logs |
72+
| `--log-level`<br>`LINUX_MCP_LOG_LEVEL` | `INFO` | Log verbosity: `DEBUG`, `INFO`, `WARNING` |
73+
| `--log-retention-days`<br>`LINUX_MCP_LOG_RETENTION_DAYS` | `10` | Days to retain log files |
7174

7275
See [Debug Logging](debugging.md) for details on log formats and locations.
7376

7477
## Examples
7578

7679
**Specify SSH settings:**
80+
7781
```bash
7882
linux-mcp-server --user admin --ssh-key-path ~/.ssh/id_rsa --verify-host-keys
7983
```
8084

8185
**Configure log access:**
86+
8287
```bash
8388
linux-mcp-server --allowed-log-paths "/var/log/messages,/var/log/secure,/var/log/audit/audit.log"
8489
```
8590

8691
**Using environment variables in a client config:**
92+
8793
```json
8894
{
8995
"env": {

docs/guarded-command-execution.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,14 @@ LINUX_MCP_TOOLSET=run_script
106106

107107
**Configure a Gatekeeper Model**
108108

109-
Set `LINUX_MCP_GATEKEEPER_MODEL` to the name of the model you want to use. Additional environment
109+
Set `LINUX_MCP_GATEKEEPER__MODEL` to the name of the model you want to use. Additional environment
110110
variables may be needed to configure credentials. See the
111111
[LiteLLM documentation](https://docs.litellm.ai/docs/providers) for details on how to configure your provider.
112112

113113
Example:
114114

115115
```sh
116-
LINUX_MCP_GATEKEEPER_MODEL=openai/chatgpt-5.2
116+
LINUX_MCP_GATEKEEPER__MODEL=openai/chatgpt-5.2
117117
OPENAI_API_KEY=<....>
118118
```
119119

eval/gatekeeper/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Runs test cases through the gatekeeper and reports results.
6464

6565
```bash
6666
# Set the gatekeeper model
67-
export LINUX_MCP_GATEKEEPER_MODEL="openrouter/anthropic/claude-3.5-sonnet"
67+
export LINUX_MCP_GATEKEEPER__MODEL="openrouter/anthropic/claude-3.5-sonnet"
6868
6969
# Run evaluation on a single file
7070
uv run eval/gatekeeper/run-eval.py testcases/selinux-port-denial.yaml -o results.yaml

eval/gatekeeper/run-eval-models-corp.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ get_MC_base_url() {
3838
}
3939

4040
[[ -z "${MODEL}" ]] && echo "$MODEL must be set" && exit 1
41-
export LINUX_MCP_GATEKEEPER_MODEL
42-
LINUX_MCP_GATEKEEPER_MODEL="openai/$MODEL"
41+
export LINUX_MCP_GATEKEEPER__MODEL
42+
LINUX_MCP_GATEKEEPER__MODEL="openai/$MODEL"
4343
export OPENAI_API_BASE
4444
OPENAI_API_BASE="$(get_MC_base_url "$MODEL")"
4545

eval/gatekeeper/run-eval.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,9 @@ def main(
219219
typer.echo("Must specify either a test case file or --all.", err=True)
220220
raise typer.Exit(code=1)
221221

222-
if "LINUX_MCP_GATEKEEPER_MODEL" not in os.environ:
222+
if "LINUX_MCP_GATEKEEPER__MODEL" not in os.environ and "LINUX_MCP_GATEKEEPER__MODEL" not in os.environ:
223223
typer.echo(
224-
"Please set the LINUX_MCP_GATEKEEPER_MODEL environment variable to specify the Gatekeeper model to use."
224+
"Please set the LINUX_MCP_GATEKEEPER__MODEL environment variable to specify the Gatekeeper model to use."
225225
)
226226
raise typer.Exit(code=1)
227227

src/linux_mcp_server/config.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
"""Settings for linux-mcp-server"""
22

3+
import logging
4+
import os
35
import sys
46

57
from pathlib import Path
8+
from typing import Any
69

710
from pydantic import Field
11+
from pydantic import model_validator
812
from pydantic import SecretStr
913
from pydantic_settings import BaseSettings
1014
from pydantic_settings import SettingsConfigDict
@@ -13,6 +17,9 @@
1317
from linux_mcp_server.utils.types import UpperCase
1418

1519

20+
logger = logging.getLogger(__name__)
21+
22+
1623
class Transport(StrEnum):
1724
stdio = "stdio"
1825
http = "http"
@@ -27,6 +34,20 @@ class Toolset(StrEnum):
2734
BOTH = "both"
2835

2936

37+
class ReasoningEffort(StrEnum):
38+
"""Reasoning effort levels for the gatekeeper model."""
39+
40+
NONE = "none"
41+
MINIMAL = "minimal"
42+
LOW = "low"
43+
MEDIUM = "medium"
44+
HIGH = "high"
45+
XHIGH = "xhigh"
46+
DEFAULT = "default"
47+
DISABLE_THINKING = "disable_thinking"
48+
ENABLE_THINKING = "enable_thinking"
49+
50+
3051
class AuthProvider(StrEnum):
3152
"""Authentication provider types."""
3253

@@ -78,6 +99,24 @@ class AuthConfig(BaseSettings):
7899
introspection: IntrospectionAuthConfig | None = None
79100

80101

102+
class GatekeeperConfig(BaseSettings):
103+
"""Gatekeeper Model configuration"""
104+
105+
model: str | None = None
106+
107+
# model quantization (e.g. fp8, bf16 - only supported for openrouter)
108+
quantization: str | None = None
109+
110+
# reasoning effort
111+
reasoning_effort: ReasoningEffort | None = None
112+
113+
# Whether we should use structured output (default, autodetect support)
114+
structured_output: bool | None = None
115+
116+
# Temperature for gatekeeper model
117+
temperature: float = 0.0
118+
119+
81120
class Config(BaseSettings):
82121
# The '_' is required in the env_prefix, otherwise, pydantic would
83122
# interpret the prefix as LINUX_MCPLOG_DIR, instead of LINUX_MCP_LOG_DIR
@@ -127,8 +166,7 @@ class Config(BaseSettings):
127166
# What tools are available
128167
toolset: Toolset = Toolset.FIXED
129168

130-
# Gatekeeper model (required for run_script tools)
131-
gatekeeper_model: str | None = None
169+
gatekeeper: GatekeeperConfig = Field(default_factory=GatekeeperConfig)
132170

133171
# Command execution timeout (applies to both local and remote commands)
134172
command_timeout: int = 30 # Timeout in seconds; prevents hung commands
@@ -165,9 +203,25 @@ def transport_kwargs(self):
165203
#
166204
# @model_validator(mode="after")
167205
# def validate_gatekeeper_model(self):
168-
# if self.toolset != Toolset.FIXED and self.gatekeeper_model is None:
169-
# raise ValueError('gatekeeper_model must be set unless the toolset is "fixed"')
206+
# if self.toolset != Toolset.FIXED and self.gatekeeper.model is None:
207+
# raise ValueError('gatekeeper.model must be set unless the toolset is "fixed"')
170208
# return self
171209

210+
@model_validator(mode="before")
211+
@classmethod
212+
def handle_deprecated_aliases(cls, data: Any) -> Any:
213+
if isinstance(data, dict):
214+
old_value = os.environ.get("LINUX_MCP_GATEKEEPER_MODEL")
215+
if old_value is not None:
216+
logger.warning(
217+
"LINUX_MCP_GATEKEEPER_MODEL is deprecated. Please use LINUX_MCP_GATEKEEPER__MODEL instead.",
218+
)
219+
220+
gatekeeper_data = data.setdefault("gatekeeper", {})
221+
if isinstance(gatekeeper_data, dict) and "model" not in gatekeeper_data:
222+
gatekeeper_data["model"] = old_value
223+
224+
return data
225+
172226

173227
CONFIG = Config()

0 commit comments

Comments
 (0)