Skip to content

feat: add MCP server mode, command fallback, traffic capture, and E2E tests#6

Merged
appleboy merged 17 commits intomainfrom
phase3-4
Mar 28, 2026
Merged

feat: add MCP server mode, command fallback, traffic capture, and E2E tests#6
appleboy merged 17 commits intomainfrom
phase3-4

Conversation

@appleboy
Copy link
Copy Markdown
Member

Summary

  • MCP Server mode: Implement mcp-server and install-mcp-server commands using go-sdk, exposing scan and get_scan_results tools with background periodic scanning
  • Per-status-code errors: Analysis API now returns specific messages for 401 (unauthorized), 413 (payload too large), 429 (rate limited), and 5xx (server unreachable)
  • Command fallback paths: resolveCommand() searches nvm, npm-global, yarn, pyenv, cargo, homebrew, and ~/.local/bin when exec.LookPath fails
  • Traffic capture: CaptureTransport wraps any MCP transport to record all sent/received JSON-RPC messages for debugging
  • E2E test infrastructure: Math (clean) and Weather (malicious) test MCP servers with 5 end-to-end tests

New packages/files

Path Description
internal/mcpserver/server.go MCP server implementation with go-sdk
internal/mcpserver/install.go Install command for Claude Desktop config
internal/mcpclient/resolve.go Command resolution with fallback dirs
internal/mcpclient/capture.go Traffic capture transport wrapper
internal/testserver/ Math and Weather test MCP servers
internal/e2e/ E2E test suite (5 tests)

Test plan

  • make test — all 80+ tests pass with race detector
  • make lint — 0 lint issues
  • make build — compiles successfully
  • E2E: clean math server → 0 issues detected
  • E2E: malicious weather server → W001 + E005 detected
  • E2E: inspect-only mode returns signatures without issues
  • E2E: JSON and text output formatting verified

🤖 Generated with Claude Code

appleboy and others added 2 commits March 24, 2026 23:16
- Introduce clientError type to distinguish non-retryable 4xx from
  retryable 5xx HTTP errors in upload and analysis packages
- Populate ScanMetadata.Version in upload payload
- Add test suite for internal/pipeline with 10 tests (96.5% coverage)
- Add test suite for internal/output with 8 tests (88.3% coverage)
- Add test suite for internal/analysis with 7 tests (88.7% coverage)
- Add test suite for internal/upload with 7 tests (86.8% coverage)
- Add test suite for internal/tlsutil with 5 tests (87.5% coverage)
- Add test suite for internal/cli with 4 tests (46.9% coverage)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… tests

- Implement MCP server mode using go-sdk with scan and get_scan_results
  tools, background periodic scanning, and install command
- Add per-status-code error messages in analysis API (401, 413, 429)
- Add command resolution fallback paths for nvm, pyenv, cargo, homebrew
- Add traffic capture wrapper for MCP transport debugging
- Build E2E test infrastructure with math and weather test MCP servers
- Add 5 E2E tests verifying full scan pipeline end-to-end

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 24, 2026 15:31
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds MCP server support and supporting infrastructure so agent-scanner can run as an MCP server (with install helpers), improve command resolution, capture MCP traffic for debugging, and validate behavior via unit + E2E tests.

Changes:

  • Implement MCP server mode (mcp-server) with scan / get_scan_results tools and optional background scanning, plus an install command for Claude Desktop config.
  • Add command resolution fallback paths and an MCP transport wrapper for JSON-RPC traffic capture.
  • Expand automated testing: uploader/analyzer retry semantics, output formatting, TLS utils, pipeline behavior, MCP server behavior, and E2E coverage with purpose-built test servers.

Reviewed changes

Copilot reviewed 26 out of 27 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
internal/upload/uploader.go Adds scan metadata + retry behavior tweaks for control-server uploads.
internal/upload/uploader_test.go Tests upload behavior: success, retries, headers, metadata, cancellation.
internal/tlsutil/tlsutil_test.go Adds tests for transport cloning and TLS config cloning behavior.
internal/testserver/protocol.go Minimal JSON-RPC protocol helpers for test servers.
internal/testserver/math_server.go Clean MCP test server (math tools) for E2E coverage.
internal/testserver/weather_server.go “Malicious” MCP test server (suspicious tool descriptions) for E2E coverage.
internal/pipeline/pipeline_test.go Adds unit tests validating pipeline stage execution and error tolerance.
internal/output/output_test.go Adds tests for text + JSON formatting and truncation behavior.
internal/mcpserver/server.go Implements MCP server + tool handlers + background scan loop + cached results.
internal/mcpserver/server_test.go Validates MCP tool registration, scan caching, summary building, concurrency.
internal/mcpserver/install.go Implements Claude Desktop config installation helper.
internal/mcpserver/install_test.go Tests default config path + install behavior across scenarios.
internal/mcpclient/stdio.go Switches stdio command lookup to resolveCommand fallback resolver.
internal/mcpclient/resolve.go Implements PATH + fallback-dir command resolution.
internal/mcpclient/resolve_test.go Tests command resolution fallback + executable detection.
internal/mcpclient/capture.go Implements transport wrapper capturing sent/received JSON-RPC messages.
internal/mcpclient/capture_test.go Tests capture wrapper behavior and message recording semantics.
internal/e2e/e2e_test.go End-to-end pipeline tests against built test MCP servers + formatter checks.
internal/cli/mcpserver.go Wires CLI mcp-server command to pipeline + MCP server runner.
internal/cli/install.go Wires CLI install-mcp-server command to installer.
internal/cli/cli_test.go Adds tests for parsing control-server CLI args.
internal/analysis/analyzer.go Adds per-status-code error messaging + retry/no-retry semantics.
internal/analysis/analyzer_test.go Tests analyzer behavior: retries, error handling, nil signature skipping.
cmd/testserver-math/main.go Entry point for math MCP test server binary.
cmd/testserver-weather/main.go Entry point for weather MCP test server binary.
go.mod Adds MCP go-sdk dependency (and related indirect deps).
go.sum Locks new dependency checksums.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Return errors instead of nil on JSON marshal failures in MCP server handlers
- Copy slices in ScanState.Set/Get to prevent callers from mutating cached data
- Deep-clone server configs before redaction to avoid mutating caller results
- Fix install command to assign default config path before calling InstallServer
- Remove unused ClientName field and --client-name CLI flag
- Simplify uploader test to use errors.As directly, remove containsClientError
- Add runtime.GOOS skip guards for Windows on Unix-specific resolve tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Search system fallback dirs even when home directory is unavailable
- Deep-copy JSONRPCMessage in capture transport to prevent mutation
- Return error when mcpServers config key has unexpected type

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Use cancelable context in RunServer so background goroutines exit cleanly
- Deep-copy messages in CaptureTransport.Messages() to prevent caller mutation
- Clone ScanError pointers before redaction to avoid mutating caller data
- Compare transport defaults against http.DefaultTransport instead of hardcoded values
- Add Windows .exe suffix for E2E test server binaries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The test previously only exercised sequential Set/Get. Add goroutine-based
concurrent access to actually validate thread safety under the race detector.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…shutdown

Thread cmd.Context() from the CLI through to RunServer so that
SIGINT/cancellation propagates to background scan goroutines.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Gate E2E tests behind testing.Short() to avoid slow go build in CI
- Treat empty/whitespace-only config files as empty objects in install

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@appleboy appleboy requested a review from Copilot March 25, 2026 13:05
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…eaks

Ensure the wrapped receive channel is created exactly once, so
repeated Receive() calls return the same channel instead of spawning
extra goroutines that split messages unpredictably.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
testing.Short() panics when called before flag.Parse() in TestMain.
Use an environment variable gate instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Strip JSONC comments before unmarshaling in install command
- Remove duplicate status code in analyzer error messages
- Use atomic.Bool for scanCalled in server test to avoid race

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

E2E tests build external binaries and are slow. Using a build tag
excludes them from default 'go test ./...' runs. Run explicitly
with 'go test -tags e2e ./internal/e2e/...'.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Integrate changes from main (PRs #4, #5) into phase3-4 branch:
- Keep nonRetryableError type and retry guard in analyzer and uploader
- Use atomic.Bool for thread-safe test variables
- Use sanitizeBodySnippet with io.LimitReader for safe error body handling
- Use more reliable test timeouts (4s/5s instead of 1500ms)
- Remove unused statusMessage function (replaced by sanitizeBodySnippet)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 21 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

appleboy and others added 4 commits March 28, 2026 11:19
- Redact env vars and headers from scan results before caching and
  returning to MCP clients to prevent credential leakage
- Clamp negative ScanInterval to default 30m to prevent
  time.NewTicker panic from user-provided CLI flags
- Add tests for redaction behavior and negative interval handling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract shared runServer helper to eliminate duplicated stdin scanner loop
- Define DirectionSent and DirectionReceived constants for capture messages
- Use redacted results for buildSummary instead of original unredacted data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Document mcp-server command with --tool and --scan-interval options
- Document install-mcp-server command for Claude Desktop integration
- Add MCP server mode to features list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@appleboy appleboy merged commit fa793cc into main Mar 28, 2026
11 checks passed
@appleboy appleboy deleted the phase3-4 branch March 28, 2026 06:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants