Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ make build_windows_amd64 # Windows x86-64
- `internal/redact/` — Sensitive data redaction
- `internal/upload/` — Control server upload
- `internal/signed/` — macOS code signature verification
- `internal/mcpserver/` — Self-as-MCP-server mode
- `internal/cli/` — Cobra CLI commands
- `internal/version/` — Build-time version info

Expand Down
28 changes: 0 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ Inspired by [snyk/agent-scan](https://github.com/snyk/agent-scan), reimplemented
- **13 security rules** detecting prompt injections, tool shadowing, hardcoded secrets, malicious code, toxic flows, and more
- **Skill scanning** for agent skill directories containing `SKILL.md`
- **Direct scanning** from package managers (`npm:`, `pypi:`, `oci://`) and URLs (`sse://`, `streamable-http://`)
- **MCP server mode** — run agent-scanner itself as an MCP server with background periodic scanning
- **Cross-platform** support (macOS, Linux, Windows)
- **Single binary** with zero runtime dependencies

Expand Down Expand Up @@ -95,33 +94,6 @@ List tools, prompts, and resources without security analysis:
agent-scanner inspect
```

### MCP Server Mode

Run agent-scanner as an MCP server, exposing `scan` and `get_scan_results` tools:

```bash
agent-scanner mcp-server
```

Run in tool-only mode (no background scanning):

```bash
agent-scanner mcp-server --tool
```

Customize the background scan interval:

```bash
agent-scanner mcp-server --scan-interval 60
```

Install agent-scanner into Claude Desktop configuration:

```bash
agent-scanner install-mcp-server
agent-scanner install-mcp-server ~/.config/claude/claude_desktop_config.json
```

### Options

```text
Expand Down
7 changes: 0 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,12 @@ module github.com/go-authgate/agent-scanner
go 1.25.10

require (
github.com/modelcontextprotocol/go-sdk v1.6.1
github.com/spf13/cobra v1.10.2
github.com/tidwall/jsonc v0.3.3
golang.org/x/sync v0.20.0
)

require (
github.com/google/jsonschema-go v0.4.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/segmentio/encoding v0.5.4 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sys v0.45.0 // indirect
)
20 changes: 0 additions & 20 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,35 +1,15 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/jsonschema-go v0.4.3 h1:/DBOLZTfDow7pe2GmaJNhltueGTtDKICi8V8p+DQPd0=
github.com/google/jsonschema-go v0.4.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/modelcontextprotocol/go-sdk v1.6.1 h1:0zOSupjKUxPKSocPT1Wtago+mUHU2/uZ4xSOY0FGReU=
github.com/modelcontextprotocol/go-sdk v1.6.1/go.mod h1:kzm3kzFL1/+AziGOE0nUs3gvPoNxMCvkxokMkuFapXQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0=
github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/tidwall/jsonc v0.3.3 h1:RVQqL3xFfDkKKXIDsrBiVQiEpBtxoKbmMXONb2H/y2w=
github.com/tidwall/jsonc v0.3.3/go.mod h1:dw+3CIxqHi+t8eFSpzzMlcVYxKp08UP5CD8/uSFCyJE=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
14 changes: 3 additions & 11 deletions internal/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,16 @@ type ScanFlags struct {
ControlIdentifier []string
}

// MCPServerFlags holds mcp-server subcommand flags.
type MCPServerFlags struct {
Tool bool
Background bool
ScanInterval int
}

// ClientsFlags holds clients-specific flags.
type ClientsFlags struct {
CurrentOS bool
JSON bool
}

var (
commonFlags CommonFlags
scanFlags ScanFlags
mcpServerFlags MCPServerFlags
clientsFlags ClientsFlags
commonFlags CommonFlags
scanFlags ScanFlags
clientsFlags ClientsFlags
)

// addCommonFlags registers flags shared across scan/inspect commands.
Expand Down
42 changes: 0 additions & 42 deletions internal/cli/install.go

This file was deleted.

67 changes: 0 additions & 67 deletions internal/cli/mcpserver.go

This file was deleted.

2 changes: 0 additions & 2 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ func init() {
rootCmd.AddCommand(versionCmd)
rootCmd.AddCommand(newScanCmd())
rootCmd.AddCommand(newInspectCmd())
rootCmd.AddCommand(newMCPServerCmd())
rootCmd.AddCommand(newInstallCmd())
rootCmd.AddCommand(newClientsCmd())
}

Expand Down
45 changes: 42 additions & 3 deletions internal/discovery/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,58 @@ func ParseMCPConfigFile(path string) (models.MCPConfig, error) {
{"vscode_mcp", parseVSCodeMCPConfig},
}

// sawTypeError records whether a recognized config shape failed to decode
// because of a JSON type mismatch (e.g. "mcpServers": [] or a non-string
// command). That signals a malformed-but-intended config rather than one
// that simply lacks an MCP section.
var sawTypeError bool
for _, p := range parsers {
cfg, err := p.parse(data)
if err == nil && cfg != nil {
servers := cfg.GetServers()
if len(servers) > 0 {
if err != nil {
var typeErr *json.UnmarshalTypeError
if errors.As(err, &typeErr) {
sawTypeError = true
}
continue
}
if cfg != nil {
if servers := cfg.GetServers(); len(servers) > 0 {
return cfg, nil
}
}
}

// No MCP servers were found. Distinguish a valid config object that simply
// has no MCP section (parses clean → empty server set) from data we cannot
// trust: malformed configs (type mismatches) and non-object JSON
// (arrays/scalars/syntax errors) are reported as an unknown format so a
// real problem is not silently hidden as "no servers".
if !sawTypeError {
var probe map[string]any
// An explicit null for a known MCP key (e.g. {"mcpServers": null}) is a
// non-object value that decodes without a type error, so it is treated
// as malformed rather than "no servers".
if json.Unmarshal(data, &probe) == nil && !hasNullMCPKey(probe) {
return &models.ConfigWithoutMCP{}, nil
}
Comment thread
appleboy marked this conversation as resolved.
}
return &models.UnknownMCPConfig{}, nil
}

// mcpConfigKeys are the top-level keys the recognized parsers read. A config
// that declares one of these but holds an explicit null (rather than a valid
// object) is malformed rather than simply lacking an MCP section.
var mcpConfigKeys = []string{"mcpServers", "mcp", "servers"}

func hasNullMCPKey(obj map[string]any) bool {
for _, key := range mcpConfigKeys {
if v, ok := obj[key]; ok && v == nil {
return true
}
}
return false
}

func parseClaudeConfig(data []byte) (models.MCPConfig, error) {
var cfg models.ClaudeConfigFile
if err := json.Unmarshal(data, &cfg); err != nil {
Expand Down
Loading
Loading