Skip to content
khansaadPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

JAFRA-MCP-Server

MCP server that exposes JFR profiling data from Kubernetes JVM pods to an LLM agent.

What it does

  1. Exposes 9 MCP tools so an agent can discover pods, trigger/monitor recordings, and retrieve JFR analysis from any pod in the cluster.
  2. Delegates analysis to the Jafra Analyzer backend via a clean REST API — the MCP server has zero knowledge of JFR internals.
  3. Live tools (6): list_profiled_pods, get_pod_jvm_status, get_jvm_statistics, get_recording_report, get_jfr_summary (all five wired to Jafra Analyzer — list_profiled_pods now calls GET /api/v1/recordings).
  4. Stub tools (3): start_recording, stop_recording, get_recording, get_flame_graph — defined and advertised today; will be wired to the jafra-controller once its recording API is finalised.

Architecture

LLM Agent
    │  MCP (stdio / SSE / Streamable HTTP)
    ▼
JAFRA-profiler-mcp-server   ← this repo
    │  REST
    ▼
Jafra Analyzer   (GET /api/v1/recordings)
    │            (GET /api/v1/namespaces/{ns}/pods/{pod}/containers/{c}/report)
    │            (GET /api/v1/namespaces/{ns}/pods/{pod}/containers/{c}/summary)
    ▼
Jafra Agent (DaemonSet — streams JFR chunks from every node)
    │
    └─ async-profiler sidecar injected by jafra-controller (MutatingWebhook)

Documentation

MCP Tools

# Tool Status What it does
1 list_profiled_pods ✅ Live Lists all pods with JFR recordings — calls GET /api/v1/recordings
2 get_pod_jvm_status ✅ Live Profiler readiness + latest recording info
3 get_jvm_statistics ✅ Live Live heap / thread / GC snapshot (no recording needed)
4 get_recording_report ✅ Live JMC rule-based findings for a pod/time window (calls /report)
5 get_jfr_summary ✅ Live Compact LLM-ready JFR event summary (calls /summary)
6 start_recording 🔧 Stub Trigger a JFR recording — wired to jafra-controller when recording API is ready
7 stop_recording 🔧 Stub Stop an in-progress recording early
8 get_recording 🔧 Stub Poll recording status: QUEUED → RECORDING → ANALYSING → READY
9 get_flame_graph 🔧 Stub Flame graph as SVG or JSON — wired to jafra-controller when ready

Typical agent flow (current)

1. list_profiled_pods()                               → find pods with JFR recordings
2. get_jfr_summary(ns, pod, container, last="5m")     → broad performance overview
   • scope       — recording window, file metadata
   • environment — JVM version, GC config, heap/container limits
   • signals     — CPU, GC pauses, heap, locks, allocations, threads, errors
3. get_recording_report(ns, pod, container, last="5m") → JMC rule findings
   • findings["VMOperations"] score=100  → long VM operations
   • findings["GC Pressure"]  score=84  → GC is a problem
   • findings["PID-1 Rule"]   score=75  → Java running as PID 1
4. Agent correlates findings → Root Cause Analysis

Jafra Analyzer REST API (live endpoints)

GET /api/v1/recordings
    → RecordingsListResponse  (all workloads with JFR recordings — used by list_profiled_pods)

GET /api/v1/namespaces/{ns}/pods/{pod}/containers/{container}/report?last=5m
    → JafraSummaryReport  (JMC rule-based findings keyed by rule ID)

GET /api/v1/namespaces/{ns}/pods/{pod}/containers/{container}/summary?last=5m
    → JafraSummaryDocument  (raw JFR event statistics, transformed to McpSummary)

404 error handling: If pod is not found in the analyzer, get_recording_report and get_jfr_summary return isError: true with an actionable message listing all available workloads and a tip to call list_profiled_pods first.

All request/response types are defined in ProfilingModels.java.

Configuration

# src/main/resources/application.properties

# Jafra Analyzer base URL (or set JAFRA_ANALYZER_URL env var)
quarkus.rest-client.jafra-analyzer.url=${JAFRA_ANALYZER_URL:http://localhost:8080}

# Default Kubernetes namespace when callers omit it (or set PROFILER_DEFAULT_NAMESPACE)
profiler.controller.default-namespace=${PROFILER_DEFAULT_NAMESPACE:default}

Building

./mvnw clean package -DskipTests

Building the image

# linux/amd64 (for Kubernetes / OCP deployments)
podman build --platform linux/amd64 -t quay.io/khansaad/async-profiler-mcp-server:0.1.0 .
podman push quay.io/khansaad/async-profiler-mcp-server:0.1.0

Testing

./mvnw test

Project layout

JAFRA-mcp-server/
├── Dockerfile
├── smoke_test.py                              ← end-to-end MCP handshake smoke test
├── manifests/
│   ├── async-profiler-mcp-server-kind.yaml   ← Kind deployment (jafra-system namespace)
│   ├── async-profiler-mcp-server-openshift.yaml
│   └── async-profiler-mcp-server-minikube.yaml
└── src/
    ├── main/java/io/causa/profiler/
    │   ├── dto/
    │   │   └── ProfilingModels.java          ← all DTOs: ProfilingModels, JafraSummaryReport,
    │   │                                        JafraSummaryDocument, McpSummary, RecordingMetadata…
    │   ├── mcp/
    │   │   └── ProfilingTools.java           ← all 9 @Tool methods
    │   └── service/
    │       ├── ProfilerService.java          ← interface
    │       └── ProfilerServiceClient.java    ← Jafra Analyzer REST client +
    │                                            summary_to_mcp.py logic (Java port)
    └── test/java/io/causa/profiler/mcp/
        └── ProfilingToolsTest.java           ← 19 unit tests (all 9 tools, mocked service)

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages