Skip to content

Sentinel CI Architecture

Overview

Sentinel CI is a multi-platform CI/CD intelligence layer built using the Model Context Protocol (MCP). It provides a unified interface for AI agents to interact with various CI/CD platforms (Jenkins, GitHub Actions, GitLab CI, and generic CI systems).

System Architecture

┌─────────────────────────────────────────────────────────────┐
│                        AI Agent / Client                    │
│                   (Claude, GPT, Custom AI)                  │
└───────────────────────────┬─────────────────────────────────┘
                            │
                            │ MCP Protocol (stdio/HTTP)
                            │
┌───────────────────────────▼─────────────────────────────────┐
│                     Sentinel CI Server                      │
│  ┌──────────────────────────────────────────────────────┐   │
│  │              MCP Server (FastMCP)                    │   │
│  │  - health_check()      - get_failed_stages()         │   │
│  │  - list_jobs()         - get_failed_tests()          │   │
│  │  - get_build_summary() - get_log_excerpt()           │   │
│  │  - get_metrics()       - clear_cache()               │   │
│  └──────────────────────────────────────────────────────┘   │
│                            │                                │
│  ┌─────────────────────────┴────────────────────────────┐   │
│  │            Platform Router                           │   │
│  │  (Routes requests to appropriate client)             │   │
│  └────────────────────────┬─────────────────────────────┘   │
│                            │                                │
│         ┌──────────────────┼──────────────────┐             │
│         │                  │                  │             │
│  ┌──────▼──────┐  ┌────────▼─────┐  ┌─────────▼────┐        │
│  │   Jenkins   │  │GitHub Actions│  │  GitLab CI   │        │
│  │   Client    │  │    Client    │  │    Client    │        │
│  └──────┬──────┘  └───────┬──────┘  └───────┬──────┘        │
│         │                 │                 │               │
│  ┌──────▼─────────────────▼─────────────────▼──────────┐    │
│  │              Shared Components                      │    │
│  │  ┌────────┐  ┌──────────┐  ┌─────────────────┐      │    │
│  │  │ Cache  │  │ Redactor │  │  Metrics        │      │    │
│  │  │(TTL,   │  │(Secret   │  │(Prometheus)     │      │    │
│  │  │ LRU)   │  │patterns) │  │                 │      │    │
│  │  └────────┘  └──────────┘  └─────────────────┘      │    │
│  └─────────────────────────────────────────────────────┘    │
└────────────────────────────┬────────────────────────────────┘
                             │
                             │ REST APIs
                             │
         ┌───────────────────┼───────────────────┐
         │                   │                   │
    ┌────▼────┐      ┌───────▼─────┐      ┌──────▼───┐
    │ Jenkins │      │   GitHub    │      │  GitLab  │
    │  API    │      │ Actions API │      │  CI API  │
    └─────────┘      └─────────────┘      └──────────┘

Component Details

1. MCP Server Layer

Location: sentinel_ci/server.py

Responsibilities: - Expose MCP tools for AI agents - Handle tool invocations - Route requests to appropriate platform client - Coordinate caching and metrics

Key Tools:

@app.tool()
def health_check() -> dict:
    """Check connectivity to CI platform"""

@app.tool()
def list_jobs(path: str = None) -> dict:
    """List all jobs/workflows"""

@app.tool()
def get_build_summary(job: str, build_id: str) -> dict:
    """Get summary of a specific build"""

@app.tool()
def get_failed_stages(job: str, build_id: str) -> dict:
    """Get failed stages/steps"""

@app.tool()
def get_failed_tests(job: str, build_id: str) -> dict:
    """Get failed test details"""

@app.tool()
def get_log_excerpt(job: str, build_id: str, stage: str, lines: int) -> dict:
    """Get relevant log excerpts"""

@app.tool()
def get_metrics() -> dict:
    """Get system metrics"""

@app.tool()
def clear_cache() -> dict:
    """Clear the cache"""

Configuration:

# Select platform via environment variable
CI_PLATFORM = os.getenv('CI_PLATFORM', 'jenkins')

# Platform-specific credentials
JENKINS_URL = os.getenv('JENKINS_URL')
JENKINS_TOKEN = os.getenv('JENKINS_TOKEN')
GITHUB_TOKEN = os.getenv('GITHUB_TOKEN')
GITLAB_TOKEN = os.getenv('GITLAB_TOKEN')

2. Platform Clients

All platform clients implement the AbstractCIClient interface:

class AbstractCIClient(ABC):
    @abstractmethod
    def health_check(self) -> dict:
        pass

    @abstractmethod
    def list_jobs(self, path=None) -> dict:
        pass

    @abstractmethod
    def get_build_summary(self, job: str, build_id: str) -> dict:
        pass

    # ... other methods

Jenkins Client

Location: mcps/jenkins/client.py

Features: - Classic and Pipeline job support - Blue Ocean API integration - wfapi plugin for pipeline details - JUnit test result parsing

API Endpoints Used: - /api/json - Job listing - /job/{name}/{build}/api/json - Build details - /job/{name}/{build}/wfapi/describe - Pipeline stages - /job/{name}/{build}/testReport/api/json - Test results - /job/{name}/{build}/consoleText - Build logs

Status Mapping:

color_map = {
    'blue': 'SUCCESS',
    'red': 'FAILURE',
    'yellow': 'UNSTABLE',
    'aborted': 'ABORTED',
    'notbuilt': 'NOT_BUILT',
}

GitHub Actions Client

Location: mcps/github_actions/client.py

Features: - Workflow runs - Job details - Step information - Artifact access

API Endpoints Used: - GET /repos/{owner}/{repo}/actions/runs - List runs - GET /repos/{owner}/{repo}/actions/runs/{id} - Run details - GET /repos/{owner}/{repo}/actions/runs/{id}/jobs - Job details - GET /repos/{owner}/{repo}/actions/runs/{id}/logs - Logs

Authentication:

headers = {
    'Authorization': f'Bearer {github_token}',
    'Accept': 'application/vnd.github.v3+json'
}

GitLab CI Client

Location: mcps/gitlab_ci/client.py

Features: - Pipeline jobs - Stage details - Test reports (native support) - MR pipeline tracking

API Endpoints Used: - GET /api/v4/projects/{id}/pipelines - List pipelines - GET /api/v4/projects/{id}/pipelines/{id}/jobs - Jobs - GET /api/v4/projects/{id}/pipelines/{id}/test_report - Tests - GET /api/v4/projects/{id}/jobs/{id}/trace - Logs

Authentication:

headers = {
    'PRIVATE-TOKEN': gitlab_token
}

3. Shared Components

Caching Layer

Location: shared/caching/cache.py

Features: - TTL-based expiration - LRU eviction - Thread-safe operations - Cache statistics

Implementation:

class SimpleCache:
    def __init__(self, ttl_seconds: int, max_size: int):
        self._cache = {}
        self._ttl = ttl_seconds
        self._max_size = max_size
        self._lock = threading.Lock()

    def get(self, key: str):
        # Check expiration, return value

    def set(self, key: str, value):
        # Store with timestamp, enforce max_size

Cache TTLs: - Health checks: 30 seconds - Job lists: 60 seconds - Build summaries: 120 seconds - Test results: 120 seconds

Secret Redaction

Location: shared/redaction/redactor.py

Features: - Pattern-based redaction - 12 built-in patterns - Configurable replacement text - Regex compilation

Patterns:

patterns = [
    (r'api[_-]?key[=:]\s*["\']?([a-zA-Z0-9_-]+)', '[REDACTED]'),
    (r'password[=:]\s*["\']?([^\s\'"]+)', '[REDACTED]'),
    (r'token[=:]\s*["\']?([a-zA-Z0-9_.-]+)', '[REDACTED]'),
    # ... 9 more patterns
]

Usage:

redactor = SecretRedactor()
safe_log = redactor.redact(raw_log)

Metrics Collection

Location: shared/metrics/metrics.py

Features: - Prometheus format - Counter metrics - Histogram metrics - Percentile calculation

Metrics Tracked:

# Counters
- cache_hits_total
- cache_misses_total
- api_calls_total
- errors_total

# Histograms
- api_call_duration_seconds (P50, P95, P99)
- cache_operation_duration_seconds

Export Format:

# HELP cache_hits_total Total cache hits
# TYPE cache_hits_total counter
cache_hits_total 42

# HELP api_call_duration_seconds API call duration
# TYPE api_call_duration_seconds histogram
api_call_duration_seconds_bucket{le="0.5"} 10
api_call_duration_seconds_bucket{le="1.0"} 25
...

Data Flow

Example: Get Failed Tests

1. AI Agent requests: get_failed_tests("backend-service", "123")
   ↓
2. MCP Server receives request
   ↓
3. Check cache for key: "failed_tests:backend-service:123"
   ↓
4. Cache miss → Forward to platform client
   ↓
5. Platform client calls CI API (e.g., Jenkins /testReport)
   ↓
6. Parse API response, extract failed tests
   ↓
7. Redact secrets from test output
   ↓
8. Store result in cache (TTL: 120s)
   ↓
9. Record metrics (api_calls_total++, duration_histogram)
   ↓
10. Return formatted result to AI agent

Performance Characteristics

Latency Targets

  • Cache hit: < 10ms
  • Cache miss + API call: < 2s (P95)
  • Health check: < 500ms
  • Log excerpt: < 3s (depends on log size)

Caching Strategy

Cache keys format:

{operation}:{job}:{build_id}:{extra}

Examples:
- health_check:jenkins
- list_jobs:jenkins:path=/folder
- build_summary:backend-service:123
- failed_tests:frontend-app:456

Eviction policy: 1. TTL expiration (time-based) 2. LRU when max_size reached (capacity-based)

Resource Usage

Memory: - Base: ~50MB - Per cached entry: ~10KB - Max cache: ~100MB (1000 entries × 10KB × 10 platforms)

Network: - Health checks: 1 request/30s - Job list: 1 request/60s - Build details: 1 request per unique build (cached 120s)

Security Considerations

Authentication Flow

1. User provides credentials via environment variables
   ↓
2. Sentinel CI loads credentials on startup
   ↓
3. Never logs or exposes raw credentials
   ↓
4. Uses credentials to authenticate API calls
   ↓
5. Redacts any credentials from logs/output

Read-Only Operations

All operations are read-only: - ❌ Cannot trigger builds - ❌ Cannot modify configurations - ❌ Cannot delete jobs/builds - ✅ Only fetches existing data - ✅ Safe for production use

Audit Trail

# Every API call logged
logger.info(f"API call: {method} {endpoint} user={user}")

# Metrics recorded
metrics.increment('api_calls_total', labels={'platform': 'jenkins'})

# Cache operations tracked
metrics.record('cache_operation_duration_seconds', duration)

Extensibility

Adding a New Platform

  1. Create client class:

    # mcps/my-platform/client.py
    from shared.base.abstract_client import AbstractCIClient
    
    class MyPlatformClient(AbstractCIClient):
        def __init__(self, base_url, token):
            self.base_url = base_url
            self.token = token
    
        def health_check(self):
            # Implement
            pass
    

  2. Update server router:

    # sentinel_ci/server.py
    if CI_PLATFORM == 'my-platform':
        client = MyPlatformClient(
            base_url=os.getenv('MYPLATFORM_URL'),
            token=os.getenv('MYPLATFORM_TOKEN')
        )
    

  3. Add configuration:

    # .env
    CI_PLATFORM=my-platform
    MYPLATFORM_URL=https://ci.example.com
    MYPLATFORM_TOKEN=token_here
    

Adding a New Tool

  1. Define tool in server:

    @app.tool()
    def my_new_tool(param: str) -> dict:
        """Tool description"""
        try:
            result = client.my_new_method(param)
            return {"status": "success", "data": result}
        except Exception as e:
            return {"status": "error", "error": str(e)}
    

  2. Implement in abstract client:

    @abstractmethod
    def my_new_method(self, param: str) -> dict:
        pass
    

  3. Implement in each platform client:

    def my_new_method(self, param: str) -> dict:
        # Platform-specific implementation
        pass
    

Deployment Options

Standalone Server

python -m sentinel_ci.server

Docker Container

FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "-m", "sentinel_ci.server"]

Claude Desktop Integration

{
  "mcpServers": {
    "sentinel-ci": {
      "command": "python",
      "args": ["-m", "sentinel_ci.server"],
      "env": {
        "CI_PLATFORM": "jenkins",
        "JENKINS_URL": "https://jenkins.example.com",
        "JENKINS_TOKEN": "user:token"
      }
    }
  }
}

Monitoring

Health Checks

# Check server health
curl http://localhost:8000/health

# Expected response
{
  "status": "healthy",
  "platform": "jenkins",
  "cache_stats": {...},
  "uptime_seconds": 3600
}

Metrics Endpoint

# Get Prometheus metrics
curl http://localhost:8000/metrics

# Integration with Prometheus
- job_name: 'sentinel-ci'
  static_configs:
    - targets: ['localhost:8000']

Future Architecture Enhancements

  1. Multi-platform aggregation:
  2. Query multiple platforms simultaneously
  3. Unified dashboard across platforms

  4. Webhook support:

  5. Real-time notifications
  6. Event-driven updates

  7. Advanced caching:

  8. Redis backend
  9. Distributed cache
  10. Cache warming

  11. Authentication middleware:

  12. OAuth integration
  13. API key management
  14. Role-based access control

  15. Batch operations:

  16. Bulk job queries
  17. Parallel API calls
  18. GraphQL endpoint

References