Skip to content

Go Library API

MCP-Scan can be used as a Go library for integration into other tools.

Installation

go get github.com/mcphub/mcp-scan

Quick Start

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/mcphub/mcp-scan/pkg/scanner"
)

func main() {
    // Create scanner with configuration
    cfg := scanner.Config{
        Mode:    scanner.ModeFast,
        Timeout: 5 * time.Minute,
    }

    s := scanner.New(cfg)

    // Run scan
    result, err := s.Scan(context.Background(), "./my-project")
    if err != nil {
        log.Fatal(err)
    }

    // Process results
    fmt.Printf("Found %d findings\n", result.Summary.Total)
    fmt.Printf("MSSS Score: %.1f (Level %d)\n",
        result.MSSSScore.Total,
        result.MSSSScore.Level)

    // Generate report
    report, _ := s.GenerateReport(result, "json")
    fmt.Println(string(report))
}

Types

scanner.Config

type Config struct {
    // File discovery
    Include []string // Glob patterns to include (default: **/*.py, **/*.ts, etc.)
    Exclude []string // Glob patterns to exclude (default: node_modules/**, etc.)

    // Analysis settings
    Mode    Mode          // ModeFast or ModeDeep
    Timeout time.Duration // Maximum scan duration

    // Output settings
    FailOn         types.Severity // Severity threshold for exit code 1
    Rules          []string       // Specific rules to run (empty = all)
    RedactSnippets bool           // Hide code in output

    // Baseline
    Baseline string // Path to baseline file

    // Parallelism
    Workers int // Number of workers (0 = auto)
}

scanner.Mode

type Mode string

const (
    ModeFast Mode = "fast" // Intra-procedural analysis
    ModeDeep Mode = "deep" // Inter-procedural analysis
)

scanner.Result

type Result struct {
    // Core results
    Findings []types.Finding // All detected vulnerabilities
    Summary  *types.Summary  // Aggregated statistics

    // Context
    Manifest   *types.Manifest      // Scanned files
    MCPSurface *surface.MCPSurface  // Detected MCP elements
    MSSSScore  *msss.Score          // Security score

    // Metadata
    ScanDuration   time.Duration
    Mode           Mode
    ExitCode       int
    BaselinedCount int // Findings excluded by baseline
}

types.Finding

type Finding struct {
    ID          string      `json:"id"`
    RuleID      string      `json:"rule_id"`
    Severity    Severity    `json:"severity"`
    Confidence  Confidence  `json:"confidence"`
    Language    Language    `json:"language"`
    Location    Location    `json:"location"`
    MCPContext  *MCPContext `json:"mcp_context,omitempty"`
    Trace       *TaintTrace `json:"trace,omitempty"`
    Evidence    Evidence    `json:"evidence"`
    Description string      `json:"description"`
    Remediation string      `json:"remediation"`
}

types.Severity

type Severity string

const (
    SeverityInfo     Severity = "info"
    SeverityLow      Severity = "low"
    SeverityMedium   Severity = "medium"
    SeverityHigh     Severity = "high"
    SeverityCritical Severity = "critical"
)

// Level returns numeric level for comparison
func (s Severity) Level() int

types.Location

type Location struct {
    File      string `json:"file"`
    StartLine int    `json:"start_line"`
    StartCol  int    `json:"start_col"`
    EndLine   int    `json:"end_line"`
    EndCol    int    `json:"end_col"`
}

Scanner Methods

New

func New(cfg Config) *Scanner

Creates a new Scanner instance with the given configuration.

Scan

func (s *Scanner) Scan(ctx context.Context, path string) (*Result, error)

Scans the given path and returns results. The context can be used for cancellation.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

result, err := scanner.Scan(ctx, "./project")

GenerateReport

func (s *Scanner) GenerateReport(result *Result, format string) ([]byte, error)

Generates a report in the specified format: "json", "sarif", or "evidence".

ScanFile

func (s *Scanner) ScanFile(ctx context.Context, path string, lang types.Language) (*Result, error)

Scans a single file. Useful for incremental scanning.

Examples

Custom Rule Selection

cfg := scanner.Config{
    Mode:  scanner.ModeFast,
    Rules: []string{"A", "E", "G"}, // Only RCE, Secrets, Tool Poisoning
}

s := scanner.New(cfg)
result, _ := s.Scan(ctx, "./project")

With Baseline

cfg := scanner.Config{
    Mode:     scanner.ModeFast,
    Baseline: ".mcp-scan-baseline.json",
}

s := scanner.New(cfg)
result, _ := s.Scan(ctx, "./project")

fmt.Printf("New findings: %d\n", result.Summary.Total)
fmt.Printf("Baselined: %d\n", result.BaselinedCount)

Deep Mode with Timeout

cfg := scanner.Config{
    Mode:    scanner.ModeDeep,
    Timeout: 10 * time.Minute,
    FailOn:  types.SeverityHigh,
}

s := scanner.New(cfg)
result, err := s.Scan(ctx, "./project")

if result.ExitCode != 0 {
    fmt.Println("High severity findings detected!")
}

Processing Findings

result, _ := s.Scan(ctx, "./project")

for _, finding := range result.Findings {
    fmt.Printf("[%s] %s at %s:%d\n",
        finding.Severity,
        finding.RuleID,
        finding.Location.File,
        finding.Location.StartLine)

    if finding.MCPContext != nil {
        fmt.Printf("  Tool: %s\n", finding.MCPContext.ToolName)
    }

    if finding.Trace != nil {
        fmt.Printf("  Source: %s:%d\n",
            finding.Trace.Source.File,
            finding.Trace.Source.StartLine)
    }
}

SARIF Output for CI

result, _ := s.Scan(ctx, "./project")
sarif, _ := s.GenerateReport(result, "sarif")

err := os.WriteFile("results.sarif", sarif, 0644)

Checking MSSS Compliance

result, _ := s.Scan(ctx, "./project")

score := result.MSSSScore
fmt.Printf("Score: %.1f\n", score.Total)
fmt.Printf("Level: %d\n", score.Level)
fmt.Printf("Compliant: %v\n", score.Compliant)

if score.Level < 2 {
    fmt.Println("Not enterprise-ready!")
}

MCP Surface Analysis

result, _ := s.Scan(ctx, "./project")

surface := result.MCPSurface
fmt.Printf("Transport: %s\n", surface.Transport)
fmt.Printf("Tools: %d\n", len(surface.Tools))

for _, tool := range surface.Tools {
    fmt.Printf("  - %s: %s\n", tool.Name, tool.Description)
}

Error Handling

result, err := s.Scan(ctx, "./project")
if err != nil {
    switch {
    case errors.Is(err, context.DeadlineExceeded):
        log.Fatal("Scan timeout exceeded")
    case errors.Is(err, context.Canceled):
        log.Fatal("Scan was canceled")
    default:
        log.Fatalf("Scan error: %v", err)
    }
}

Thread Safety

The Scanner is safe for concurrent use:

s := scanner.New(cfg)

var wg sync.WaitGroup
for _, project := range projects {
    wg.Add(1)
    go func(p string) {
        defer wg.Done()
        result, _ := s.Scan(ctx, p)
        // process result
    }(project)
}
wg.Wait()

Integration Examples

GitHub Action (Go)

func main() {
    cfg := scanner.Config{
        Mode:   scanner.ModeFast,
        FailOn: types.SeverityHigh,
    }

    s := scanner.New(cfg)
    result, err := s.Scan(context.Background(), ".")

    if err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v\n", err)
        os.Exit(3)
    }

    sarif, _ := s.GenerateReport(result, "sarif")
    os.WriteFile(os.Getenv("GITHUB_STEP_SUMMARY"), sarif, 0644)

    os.Exit(result.ExitCode)
}

Pre-commit Hook

func main() {
    // Get staged files
    stagedFiles := getStagedFiles()

    cfg := scanner.Config{
        Mode:    scanner.ModeFast,
        Include: stagedFiles,
        FailOn:  types.SeverityHigh,
    }

    s := scanner.New(cfg)
    result, _ := s.Scan(context.Background(), ".")

    if result.ExitCode != 0 {
        fmt.Println("Security issues found in staged files!")
        for _, f := range result.Findings {
            fmt.Printf("  %s:%d - %s\n",
                f.Location.File,
                f.Location.StartLine,
                f.Description)
        }
        os.Exit(1)
    }
}

Custom Reporter

type SlackReporter struct {
    WebhookURL string
}

func (r *SlackReporter) Report(result *scanner.Result) error {
    if result.Summary.Total == 0 {
        return nil
    }

    message := fmt.Sprintf(
        "🔒 MCP-Scan found %d issues (Score: %.1f)",
        result.Summary.Total,
        result.MSSSScore.Total)

    // Send to Slack
    return r.sendWebhook(message)
}