Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
23 changes: 22 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ mcpproxy activity export --output audit.jsonl # Export for compliance

See [docs/cli/activity-commands.md](docs/cli/activity-commands.md) for complete reference.

### Agent Token CLI
```bash
mcpproxy token create --name deploy-bot --servers github,gitlab --permissions read,write
mcpproxy token list # List all agent tokens
mcpproxy token show deploy-bot # Show token details
mcpproxy token revoke deploy-bot # Revoke a token
mcpproxy token regenerate deploy-bot # Regenerate token secret
```

See [docs/features/agent-tokens.md](docs/features/agent-tokens.md) for complete reference.

### CLI Output Formatting
```bash
mcpproxy upstream list -o json # JSON output for scripting
Expand Down Expand Up @@ -153,6 +164,7 @@ See [docs/socket-communication.md](docs/socket-communication.md) for details.
{
"listen": "127.0.0.1:8080",
"api_key": "your-secret-api-key-here",
"require_mcp_auth": false,
"enable_socket": true,
"enable_web_ui": true,
"mcpServers": [
Expand Down Expand Up @@ -220,6 +232,11 @@ See [docs/configuration.md](docs/configuration.md) for complete reference.
| `GET /api/v1/activity` | List activity records with filtering |
| `GET /api/v1/activity/{id}` | Get activity record details |
| `GET /api/v1/activity/export` | Export activity records (JSON/CSV) |
| `POST /api/v1/tokens` | Create agent token |
| `GET /api/v1/tokens` | List agent tokens |
| `GET /api/v1/tokens/{name}` | Get agent token details |
| `DELETE /api/v1/tokens/{name}` | Revoke agent token |
| `POST /api/v1/tokens/{name}/regenerate` | Regenerate agent token secret |
| `GET /events` | SSE stream for live updates |

**Authentication**: Use `X-API-Key` header or `?apikey=` query parameter.
Expand Down Expand Up @@ -322,10 +339,12 @@ See `docs/code_execution/` for complete guides:

- **Localhost-only by default**: Core server binds to `127.0.0.1:8080`
- **API key always required**: Auto-generated if not provided
- **Agent tokens**: Scoped credentials for AI agents with server and permission restrictions (`mcp_agt_` prefix, HMAC-SHA256 hashed)
- **`require_mcp_auth`**: When enabled, `/mcp` endpoint rejects unauthenticated requests (default: false for backward compatibility)
- **Quarantine system**: New servers quarantined until manually approved
- **Tool Poisoning Attack (TPA) protection**: Automatic detection of malicious descriptions

See [docs/features/security-quarantine.md](docs/features/security-quarantine.md) for details.
See [docs/features/agent-tokens.md](docs/features/agent-tokens.md) and [docs/features/security-quarantine.md](docs/features/security-quarantine.md) for details.

## Sensitive Data Detection

Expand Down Expand Up @@ -496,6 +515,8 @@ See `docs/prerelease-builds.md` for download instructions.
- BBolt database (`~/.mcpproxy/config.db`) - ActivityRecord.Metadata extension (026-pii-detection)
- Go 1.24 (toolchain go1.24.10) + Cobra (CLI), Chi router (HTTP), Zap (logging), existing cliclient, socket detection, config loader (027-status-command)
- `~/.mcpproxy/mcp_config.json` (config file), `~/.mcpproxy/config.db` (BBolt - not directly used) (027-status-command)
- Go 1.24 (toolchain go1.24.10) + Cobra (CLI), Chi router (HTTP), BBolt (storage), Zap (logging), mcp-go (MCP protocol), crypto/hmac + crypto/sha256 (token hashing) (028-agent-tokens)
- BBolt database (`~/.mcpproxy/config.db`) — new `agent_tokens` bucket (028-agent-tokens)

## Recent Changes
- 001-update-version-display: Added Go 1.24 (toolchain go1.24.10)
31 changes: 31 additions & 0 deletions cmd/mcpproxy/activity_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ var (
activityNoIcons bool // Disable emoji icons in output
activityDetectionType string // Spec 026: Filter by detection type (e.g., "aws_access_key")
activitySeverity string // Spec 026: Filter by severity level (critical, high, medium, low)
activityAgent string // Spec 028: Filter by agent token name
activityAuthType string // Spec 028: Filter by auth type (admin, agent)

// Show command flags
activityIncludeResponse bool
Expand Down Expand Up @@ -72,6 +74,8 @@ type ActivityFilter struct {
SensitiveData *bool // Spec 026: Filter by sensitive data detection
DetectionType string // Spec 026: Filter by detection type
Severity string // Spec 026: Filter by severity level
AgentName string // Spec 028: Filter by agent token name
AuthType string // Spec 028: Filter by auth type (admin, agent)
}

// Validate validates the filter options
Expand Down Expand Up @@ -144,6 +148,21 @@ func (f *ActivityFilter) Validate() error {
}
}

// Validate auth_type (Spec 028)
if f.AuthType != "" {
validAuthTypes := []string{"admin", "agent"}
valid := false
for _, at := range validAuthTypes {
if f.AuthType == at {
valid = true
break
}
}
if !valid {
return fmt.Errorf("invalid auth-type '%s': must be one of %v", f.AuthType, validAuthTypes)
}
}

// Validate time formats
if f.StartTime != "" {
if _, err := time.Parse(time.RFC3339, f.StartTime); err != nil {
Expand Down Expand Up @@ -213,6 +232,13 @@ func (f *ActivityFilter) ToQueryParams() url.Values {
if f.Severity != "" {
q.Set("severity", f.Severity)
}
// Spec 028: Agent token identity filters
if f.AgentName != "" {
q.Set("agent", f.AgentName)
}
if f.AuthType != "" {
q.Set("auth_type", f.AuthType)
}
return q
}

Expand Down Expand Up @@ -722,6 +748,9 @@ func init() {
activityListCmd.Flags().Bool("sensitive-data", false, "Filter to show only activities with sensitive data detected")
activityListCmd.Flags().StringVar(&activityDetectionType, "detection-type", "", "Filter by detection type (e.g., aws_access_key, stripe_key)")
activityListCmd.Flags().StringVar(&activitySeverity, "severity", "", "Filter by severity level: critical, high, medium, low")
// Spec 028: Agent token identity filters
activityListCmd.Flags().StringVar(&activityAgent, "agent", "", "Filter by agent token name")
activityListCmd.Flags().StringVar(&activityAuthType, "auth-type", "", "Filter by auth type: admin, agent")

// Watch command flags
activityWatchCmd.Flags().StringVarP(&activityType, "type", "t", "", "Filter by type (comma-separated): tool_call, system_start, system_stop, internal_tool_call, config_change, policy_decision, quarantine_change, server_change")
Expand Down Expand Up @@ -816,6 +845,8 @@ func runActivityList(cmd *cobra.Command, _ []string) error {
SensitiveData: sensitiveDataPtr,
DetectionType: activityDetectionType,
Severity: activitySeverity,
AgentName: activityAgent,
AuthType: activityAuthType,
}

if err := filter.Validate(); err != nil {
Expand Down
11 changes: 11 additions & 0 deletions cmd/mcpproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ var (
logDir string

// Security flags
requireMCPAuth bool
readOnlyMode bool
disableManagement bool
allowServerAdd bool
Expand Down Expand Up @@ -122,6 +123,7 @@ func main() {
serverCmd.Flags().BoolVar(&enableSocket, "enable-socket", true, "Enable Unix socket/named pipe for local IPC (default: true)")
serverCmd.Flags().BoolVar(&debugSearch, "debug-search", false, "Enable debug search tool for search relevancy debugging")
serverCmd.Flags().IntVar(&toolResponseLimit, "tool-response-limit", 0, "Tool response limit in characters (0 = disabled, default: 20000 from config)")
serverCmd.Flags().BoolVar(&requireMCPAuth, "require-mcp-auth", false, "Require authentication on /mcp endpoint (agent tokens or API key)")
serverCmd.Flags().BoolVar(&readOnlyMode, "read-only", false, "Enable read-only mode")
serverCmd.Flags().BoolVar(&disableManagement, "disable-management", false, "Disable management features")
serverCmd.Flags().BoolVar(&allowServerAdd, "allow-server-add", true, "Allow adding new servers")
Expand Down Expand Up @@ -164,6 +166,9 @@ func main() {
// Add status command
statusCmd := GetStatusCommand()

// Add token command (Spec 028: Agent tokens)
tokenCmd := GetTokenCommand()

// Add commands to root
rootCmd.AddCommand(serverCmd)
rootCmd.AddCommand(searchCmd)
Expand All @@ -178,6 +183,7 @@ func main() {
rootCmd.AddCommand(activityCmd)
rootCmd.AddCommand(tuiCmd)
rootCmd.AddCommand(statusCmd)
rootCmd.AddCommand(tokenCmd)

// Setup --help-json for machine-readable help discovery
// This must be called AFTER all commands are added
Expand Down Expand Up @@ -384,6 +390,7 @@ func runServer(cmd *cobra.Command, _ []string) error {
cmdLogDir, _ := cmd.Flags().GetString("log-dir")
cmdDebugSearch, _ := cmd.Flags().GetBool("debug-search")
cmdToolResponseLimit, _ := cmd.Flags().GetInt("tool-response-limit")
cmdRequireMCPAuth, _ := cmd.Flags().GetBool("require-mcp-auth")
cmdReadOnlyMode, _ := cmd.Flags().GetBool("read-only")
cmdDisableManagement, _ := cmd.Flags().GetBool("disable-management")
cmdAllowServerAdd, _ := cmd.Flags().GetBool("allow-server-add")
Expand Down Expand Up @@ -473,6 +480,9 @@ func runServer(cmd *cobra.Command, _ []string) error {
}

// Apply security settings from command line ONLY if explicitly set
if cmd.Flags().Changed("require-mcp-auth") {
cfg.RequireMCPAuth = cmdRequireMCPAuth
}
if cmd.Flags().Changed("read-only") {
cfg.ReadOnlyMode = cmdReadOnlyMode
}
Expand All @@ -492,6 +502,7 @@ func runServer(cmd *cobra.Command, _ []string) error {
logger.Info("Configuration loaded",
zap.String("data_dir", cfg.DataDir),
zap.Int("servers_count", len(cfg.Servers)),
zap.Bool("require_mcp_auth", cfg.RequireMCPAuth),
zap.Bool("read_only_mode", cfg.ReadOnlyMode),
zap.Bool("disable_management", cfg.DisableManagement),
zap.Bool("allow_server_add", cfg.AllowServerAdd),
Expand Down
Loading
Loading