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
95 changes: 95 additions & 0 deletions .github/workflows/helm_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
name: Lint & Test helm chart

on:
pull_request:
branches:
- main
- develop
- feature/**
- release/**
- hotfix/**
paths:
- 'charts/**'
push:
branches:
- main
- develop
- feature/**
- release/**
- hotfix/**
paths:
- 'charts/**'

concurrency:
group: '${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
cancel-in-progress: true

jobs:
set-charts:
# Required permissions
permissions:
contents: read
pull-requests: read
outputs:
charts: ${{ steps.charts.outputs.changes }}
name: "Set Charts"
runs-on: [ubuntu-latest]
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v2
id: charts
with:
base: ${{ github.ref_name }}
filters: |
sysdig-mcp:
- 'charts/sysdig-mcp/**'
lint-charts:
needs: set-charts
name: Lint new helm charts
runs-on: [ubuntu-latest]
strategy:
matrix:
chart: ${{ fromJSON(needs.set-charts.outputs.charts) }}
# When set to true, GitHub cancels all in-progress jobs if any matrix job fails.
fail-fast: false
# The maximum number of jobs that can run simultaneously
max-parallel: 3
steps:

- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: v3.5.0

- uses: actions/setup-python@v4
with:
python-version: '3.10'
check-latest: true

- name: Set up chart-testing
uses: helm/[email protected]

- name: Run chart-testing (list-changed)
id: list-changed
run: |
changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }} --chart-dirs charts)
if [[ -n "$changed" ]]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
fi

- name: Run chart-testing (lint)
if: steps.list-changed.outputs.changed == 'true'
run: ct lint --target-branch ${{ github.event.repository.default_branch }} --chart-dirs charts

- name: Create kind cluster
if: steps.list-changed.outputs.changed == 'true'
uses: helm/[email protected]

- name: Run chart-testing (install)
if: steps.list-changed.outputs.changed == 'true'
run: ct install --target-branch ${{ github.event.repository.default_branch }} --chart-dirs charts
55 changes: 49 additions & 6 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
name: Publish Docker image

on:
Expand All @@ -6,22 +7,36 @@ on:
- main
paths:
- pyproject.toml
- Dockerfile
- '*.py'
- tests/**
- tools/**
- utils/**
workflow_dispatch:
inputs:
version:
description: "Version to publish"
required: false
default: "latest"
type: string

concurrency:
group: '${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
cancel-in-progress: true

jobs:
tests:
permissions:
checks: write
pull-requests: write
contents: write
uses: ./.github/workflows/test.yaml
secrets: inherit
push_to_registry:
name: Push Docker image to GitHub Packages
runs-on: ubuntu-latest
needs: tests
permissions:
contents: read # required for actions/checkout
packages: write # required for pushing to ghcr.io
id-token: write # required for signing with cosign
outputs:
version: ${{ steps.extract_version.outputs.VERSION }}
tag: ${{ steps.extract_version.outputs.TAG }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
Expand All @@ -31,6 +46,8 @@ jobs:
run: |
VERSION=$(grep 'version =' pyproject.toml | sed -e 's/version = "\(.*\)"/\1/')-$(echo $GITHUB_SHA | cut -c1-7)
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
TAG=v$(grep 'version =' pyproject.toml | sed -e 's/version = "\(.*\)"/\1/')
echo "TAG=$TAG" >> "$GITHUB_OUTPUT"

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
Expand Down Expand Up @@ -61,3 +78,29 @@ jobs:
ghcr.io/sysdiglabs/sysdig-mcp-server:v${{ steps.extract_version.outputs.VERSION }}
DIGEST: ${{ steps.build-and-push.outputs.digest }}
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}

tag_release:
name: Tag Release
runs-on: ubuntu-latest
needs: push_to_registry
steps:
- name: Check out the repo
uses: actions/checkout@v4

- name: Get tag version
id: semantic_release
uses: anothrNick/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEFAULT_BUMP: "patch"
TAG_CONTEXT: ${{ (github.base_ref != 'main') && 'branch' || 'repo' }}
PRERELEASE_SUFFIX: "beta"
PRERELEASE: ${{ (github.base_ref != 'main') && 'true' || 'false' }}
DRY_RUN: false
INITIAL_VERSION: ${{ needs.push_to_registry.outputs.tag }}

- name: Summary
run: |
echo "## Release Summary
- Tag: ${{ steps.semantic_release.outputs.tag }}
- Docker Image: ghcr.io/sysdiglabs/sysdig-mcp-server:v${{ needs.push_to_registry.outputs.version }}" >> $GITHUB_STEP_SUMMARY
73 changes: 73 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
---
name: Test

on:
push:
branches:
- main
- develop
- feature/**
- release/**
- hotfix/**
paths:
- pyproject.toml
- Dockerfile
- '*.py'
- tests/**
- tools/**
- utils/**
pull_request:
paths:
- pyproject.toml
- Dockerfile
- '*.py'
- tests/**
- tools/**
- utils/**
workflow_call:

concurrency:
group: '${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
cancel-in-progress: true

jobs:
test:
Expand Down Expand Up @@ -34,3 +58,52 @@ jobs:

- name: Run Unit Tests
run: make test

pre_release:
name: Tag Release
runs-on: ubuntu-latest
needs: test
permissions:
contents: write # required for creating a tag
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }} # checkout the correct branch name
fetch-depth: 0

- name: Extract current version
id: pyproject_version
run: |
TAG=v$(grep 'version =' pyproject.toml | sed -e 's/version = "\(.*\)"/\1/')
echo "TAG=$TAG" >> "$GITHUB_OUTPUT"

- name: Get tag version
id: semantic_release
uses: anothrNick/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEFAULT_BUMP: "patch"
TAG_CONTEXT: ${{ (github.base_ref != 'main') && 'branch' || 'repo' }}
PRERELEASE_SUFFIX: "beta"
PRERELEASE: ${{ (github.base_ref != 'main') && 'true' || 'false' }}
DRY_RUN: true
INITIAL_VERSION: ${{ steps.pyproject_version.outputs.TAG }}

- name: Compare versions
run: |
echo "Current version: ${{ steps.pyproject_version.outputs.TAG }}"
echo "New version: ${{ steps.semantic_release.outputs.tag }}"
if [ "${{ steps.pyproject_version.outputs.TAG }}" != "${{ steps.semantic_release.outputs.tag }}" ]; then
echo "### Version mismatch detected! :warning:
Current pyproject version: ${{ steps.pyproject_version.outputs.TAG }}
New Tag version: **${{ steps.semantic_release.outputs.tag }}**
Current Tag: ${{ steps.semantic_release.outputs.old_tag }}
Please update the version in pyproject.toml." >> $GITHUB_STEP_SUMMARY
exit 1
else
echo "### Version match confirmed! :rocket:
Current pyproject version: ${{ steps.pyproject_version.outputs.TAG }}
New Tag version: **${{ steps.semantic_release.outputs.tag }}**
The version is up-to-date." >> $GITHUB_STEP_SUMMARY
fi
84 changes: 78 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# MCP Server

## Table of contents

- [MCP Server](#mcp-server)
- [Table of contents](#table-of-contents)
- [Description](#description)
- [Available Tools](#available-tools)
- [Requirements](#requirements)
- [UV Setup](#uv-setup)
- [Configuration](#configuration)
- [`app_config.yaml`](#app_configyaml)
- [Environment Variables](#environment-variables)
- [Running the Server](#running-the-server)
- [Docker](#docker)
- [K8s Deployment](#k8s-deployment)
- [UV](#uv)
- [Client Configuration](#client-configuration)
- [Authentication](#authentication)
- [URL](#url)
- [Claude Desktop App](#claude-desktop-app)
- [MCP Inspector](#mcp-inspector)

## Description

This is an implementation of an [MCP (Model Context Protocol) Server](https://modelcontextprotocol.io/quickstart/server) to allow different LLMs to query information from Sysdig Secure platform. **It is still in early development and not yet ready for production use.** New endpoints and functionalities will be added over time. The goal is to provide a simple and easy-to-use interface for querying information from Sysdig Secure platform using LLMs.
Expand Down Expand Up @@ -73,10 +94,6 @@ source .venv/bin/activate

This will create a virtual environment using `uv` and install the required dependencies.

### Sysdig SDK

You will need the Sysdig-SDK. You can find it in the `build` directory as a `.tar.gz` file that will be used by UV to install the package.

## Configuration

The application can be configured via the `app_config.yaml` file and environment variables.
Expand All @@ -93,7 +110,7 @@ This file contains the main configuration for the application, including:

The following environment variables are required for configuring the Sysdig SDK:

- `SYSDIG_HOST`: The URL of your Sysdig Secure instance (e.g., `https://secure.sysdig.com`).
- `SYSDIG_HOST`: The URL of your Sysdig Secure instance (e.g., `https://us2.app.sysdig.com`).
- `SYSDIG_SECURE_TOKEN`: Your Sysdig Secure API token.

You can find your API token in the Sysdig Secure UI under **Settings > Sysdig Secure API**. Make sure to copy the token as it will not be shown again.
Expand All @@ -105,9 +122,11 @@ You can set these variables in your shell or in a `.env` file.

You can also use `MCP_TRANSPORT` to override the transport protocol set in `app_config.yaml`.

> All of this env variables have precedence over the fields configured in the app_config.yaml.

## Running the Server

You can run the MCP server using either Docker or `uv`.
You can run the MCP server using either Docker, `uv` or install it in your K8s cluster with helm.

### Docker

Expand All @@ -129,6 +148,51 @@ By default, the server will run using the `stdio` transport. To use the `streama
docker run -e MCP_TRANSPORT=streamable-http -e SYSDIG_HOST=<your_sysdig_host> -e SYSDIG_SECURE_TOKEN=<your_sysdig_secure_api_token> -p 8080:8080 sysdig-mcp-server
```

### K8s Deployment

If you want to run the Sysdig MCP server in a K8s cluster you can use the helm chart provided in the `charts/sysdig-mcp` path

Modify the `values.yaml`

```yaml
# Example values.yaml
---
sysdig:
secrets:
create: true
# If enabled, the secrets will be mounted as environment variables
secureAPIToken: "<your_sysdig_secure_api_token>"
mcp:
transport: "streamable-http"
# You can set the Sysdig Tenant URL at this level or below in the app_config configmap
host: "https://us2.app.sysdig.com" # <your_sysdig_host> "https://eu1.app.sysdig.com"

configMap:
enabled: true
app_config: |
# Sysdig MCP Server Configuration
# This file is used to configure the Sysdig MCP server.
# You can add your custom configuration here.
app:
host: "0.0.0.0"
port: 8080
log_level: "error"

sysdig:
host: "https://us2.app.sysdig.com" # <your_sysdig_host> "https://eu1.app.sysdig.com"

mcp:
transport: streamable-http
host: "0.0.0.0"
port: 8080
```

Install the chart

```bash,copy
helm upgrade --install sysdig-mcp ./charts/sysdig-mcp/ -n sysdig-mcp -f charts/sysdig-mcp/values.yaml
```

### UV

To run the server using `uv`, first set up the environment as described in the [UV Setup](#uv-setup) section. Then, run the `main.py` script:
Expand Down Expand Up @@ -233,3 +297,11 @@ For the Claude Desktop app, you can manually configure the MCP server by editing
- Replace `<path_to_your_sysdig_mcp_server_directory>` with the absolute path to the `sysdig-mcp-server` directory.

4. **Save the file** and restart the Claude Desktop app for the changes to take effect.

### MCP Inspector

1. Run the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) locally
2. Select the transport type and have the Sysdig MCP server running accordingly.
3. Pass the Authorization header if using "streamable-http" or the SYSDIG_SECURE_API_TOKEN env var if using "stdio"

![mcp-inspector](./docs/assets/mcp-inspector.png)
Loading
Loading