Files
gnoma/internal/config/config.go
T
vikingowl d2d79d65da feat(m8): MCP client, tool replaceability, and plugin system
Complete the remaining M8 extensibility deliverables:

- MCP client with JSON-RPC 2.0 over stdio transport, protocol
  lifecycle (initialize/tools-list/tools-call), and process group
  management for clean shutdown
- MCP tool adapter implementing tool.Tool with mcp__{server}__{tool}
  naming convention and replace_default for swapping built-in tools
- MCP manager for multi-server orchestration with parallel startup,
  tool discovery, and registry integration
- Plugin system with plugin.json manifest (name/version/capabilities),
  directory-based discovery (global + project scopes with precedence),
  loader that merges skills/hooks/MCP configs into existing registries,
  and install/uninstall/list lifecycle manager
- Config additions: MCPServerConfig, PluginsSection with opt-in/opt-out
  enabled/disabled resolution
- TUI /plugins command for listing installed plugins
- 54 tests across internal/mcp and internal/plugin packages
2026-04-12 03:09:05 +02:00

166 lines
4.3 KiB
Go

package config
import "time"
// Config is the top-level configuration.
type Config struct {
Provider ProviderSection `toml:"provider"`
Permission PermissionSection `toml:"permission"`
Tools ToolsSection `toml:"tools"`
RateLimits RateLimitSection `toml:"rate_limits"`
Security SecuritySection `toml:"security"`
Session SessionSection `toml:"session"`
Hooks []HookConfig `toml:"hooks"`
MCPServers []MCPServerConfig `toml:"mcp_servers"`
Plugins PluginsSection `toml:"plugins"`
}
// MCPServerConfig defines an MCP server to start and connect to.
//
// Example:
//
// [[mcp_servers]]
// name = "git"
// command = "mcp-server-git"
// args = ["--repo", "."]
// env = { GIT_DIR = ".git" }
// timeout = "30s"
// replace_default = ["bash"]
type MCPServerConfig struct {
Name string `toml:"name"`
Command string `toml:"command"`
Args []string `toml:"args"`
Env map[string]string `toml:"env"`
Timeout string `toml:"timeout"`
ReplaceDefault []string `toml:"replace_default"`
}
// PluginsSection controls plugin loading.
//
// Example:
//
// [plugins]
// enabled = ["git-tools", "docker-tools"]
// disabled = ["experimental-plugin"]
type PluginsSection struct {
Enabled []string `toml:"enabled"`
Disabled []string `toml:"disabled"`
}
// HookConfig is a single hook entry from TOML config.
//
// Example:
//
// [[hooks]]
// name = "block-dangerous-bash"
// event = "pre_tool_use"
// type = "command"
// exec = "bash-safety-check.sh"
// tool_pattern = "bash*"
// timeout = "10s"
// fail_open = false
type HookConfig struct {
Name string `toml:"name"`
Event string `toml:"event"`
Type string `toml:"type"`
Exec string `toml:"exec"`
Timeout string `toml:"timeout"`
FailOpen bool `toml:"fail_open"`
ToolPattern string `toml:"tool_pattern"`
}
type SessionSection struct {
MaxKeep int `toml:"max_keep"`
}
// SecuritySection configures the secret scanner and firewall.
//
// Example config:
//
// [security]
// entropy_threshold = 4.5
//
// [[security.patterns]]
// name = "internal_token"
// regex = "mycompany_[a-zA-Z0-9]{32}"
// action = "redact"
type SecuritySection struct {
EntropyThreshold float64 `toml:"entropy_threshold"`
Patterns []PatternConfig `toml:"patterns"`
}
type PatternConfig struct {
Name string `toml:"name"`
Regex string `toml:"regex"`
Action string `toml:"action"` // "redact" (default), "block", "warn"
}
type PermissionSection struct {
Mode string `toml:"mode"`
Rules []PermissionRule `toml:"rules"`
}
type PermissionRule struct {
Tool string `toml:"tool"`
Pattern string `toml:"pattern"`
Action string `toml:"action"`
}
type ProviderSection struct {
Default string `toml:"default"`
Model string `toml:"model"`
MaxTokens int64 `toml:"max_tokens"`
Temperature *float64 `toml:"temperature"` // TODO(M8): wire to provider.Request.Temperature
APIKeys map[string]string `toml:"api_keys"`
Endpoints map[string]string `toml:"endpoints"`
}
type ToolsSection struct {
BashTimeout Duration `toml:"bash_timeout"`
MaxFileSize int64 `toml:"max_file_size"` // TODO(M8): wire to fs tool WithMaxFileSize option
}
// RateLimitSection allows overriding default rate limits per provider.
//
// Example config:
//
// [rate_limits.mistral]
// tier = "starter"
// rps = 1
// spend_cap = 20.0
//
// [rate_limits.anthropic]
// tier = "tier2"
// rpm = 1000
// itpm = 450000
// otpm = 90000
type RateLimitSection map[string]RateLimitOverride
type RateLimitOverride struct {
Tier string `toml:"tier"`
RPS float64 `toml:"rps"`
RPM int `toml:"rpm"`
RPD int `toml:"rpd"`
TPM int `toml:"tpm"`
ITPM int `toml:"itpm"`
OTPM int `toml:"otpm"`
TokensMonth int64 `toml:"tokens_month"`
SpendCap float64 `toml:"spend_cap"`
}
// Duration wraps time.Duration for TOML string parsing (e.g. "30s", "5m").
type Duration time.Duration
func (d *Duration) UnmarshalText(text []byte) error {
parsed, err := time.ParseDuration(string(text))
if err != nil {
return err
}
*d = Duration(parsed)
return nil
}
func (d Duration) Duration() time.Duration {
return time.Duration(d)
}