graylog-mcp 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. graylog_mcp-0.3.0/.dockerignore +8 -0
  2. graylog_mcp-0.3.0/.editorconfig +12 -0
  3. graylog_mcp-0.3.0/.github/CODEOWNERS +1 -0
  4. graylog_mcp-0.3.0/.github/ISSUE_TEMPLATE/bug_report.yml +34 -0
  5. graylog_mcp-0.3.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  6. graylog_mcp-0.3.0/.github/ISSUE_TEMPLATE/feature_request.yml +16 -0
  7. graylog_mcp-0.3.0/.github/dependabot.yml +23 -0
  8. graylog_mcp-0.3.0/.github/pull_request_template.md +15 -0
  9. graylog_mcp-0.3.0/.github/workflows/ci.yml +156 -0
  10. graylog_mcp-0.3.0/.github/workflows/claude.yml +48 -0
  11. graylog_mcp-0.3.0/.github/workflows/codeql.yml +34 -0
  12. graylog_mcp-0.3.0/.github/workflows/integration.yml +53 -0
  13. graylog_mcp-0.3.0/.github/workflows/release.yml +96 -0
  14. graylog_mcp-0.3.0/.gitignore +9 -0
  15. graylog_mcp-0.3.0/.pre-commit-config.yaml +26 -0
  16. graylog_mcp-0.3.0/CHANGELOG.md +238 -0
  17. graylog_mcp-0.3.0/CLAUDE.md +27 -0
  18. graylog_mcp-0.3.0/CONTRIBUTING.md +46 -0
  19. graylog_mcp-0.3.0/Dockerfile +22 -0
  20. graylog_mcp-0.3.0/LICENSE +21 -0
  21. graylog_mcp-0.3.0/PKG-INFO +879 -0
  22. graylog_mcp-0.3.0/README.md +852 -0
  23. graylog_mcp-0.3.0/SECURITY.md +24 -0
  24. graylog_mcp-0.3.0/examples/config.toml +134 -0
  25. graylog_mcp-0.3.0/pyproject.toml +92 -0
  26. graylog_mcp-0.3.0/src/graylog_mcp/__init__.py +3 -0
  27. graylog_mcp-0.3.0/src/graylog_mcp/__main__.py +1246 -0
  28. graylog_mcp-0.3.0/src/graylog_mcp/admin/__init__.py +1 -0
  29. graylog_mcp-0.3.0/src/graylog_mcp/admin/app.py +948 -0
  30. graylog_mcp-0.3.0/src/graylog_mcp/admin/static/index.html +1758 -0
  31. graylog_mcp-0.3.0/src/graylog_mcp/ask/__init__.py +1 -0
  32. graylog_mcp-0.3.0/src/graylog_mcp/ask/flow.py +91 -0
  33. graylog_mcp-0.3.0/src/graylog_mcp/ask/jev.py +70 -0
  34. graylog_mcp-0.3.0/src/graylog_mcp/ask/pipeline.py +162 -0
  35. graylog_mcp-0.3.0/src/graylog_mcp/ask/render.py +59 -0
  36. graylog_mcp-0.3.0/src/graylog_mcp/ask/understand.py +90 -0
  37. graylog_mcp-0.3.0/src/graylog_mcp/backends/__init__.py +500 -0
  38. graylog_mcp-0.3.0/src/graylog_mcp/backends/base.py +89 -0
  39. graylog_mcp-0.3.0/src/graylog_mcp/backends/scripting.py +95 -0
  40. graylog_mcp-0.3.0/src/graylog_mcp/backends/universal.py +40 -0
  41. graylog_mcp-0.3.0/src/graylog_mcp/backends/views.py +180 -0
  42. graylog_mcp-0.3.0/src/graylog_mcp/client.py +302 -0
  43. graylog_mcp-0.3.0/src/graylog_mcp/config.py +1517 -0
  44. graylog_mcp-0.3.0/src/graylog_mcp/focus.py +59 -0
  45. graylog_mcp-0.3.0/src/graylog_mcp/operations.py +113 -0
  46. graylog_mcp-0.3.0/src/graylog_mcp/rca.py +870 -0
  47. graylog_mcp-0.3.0/src/graylog_mcp/recover.py +707 -0
  48. graylog_mcp-0.3.0/src/graylog_mcp/redact.py +447 -0
  49. graylog_mcp-0.3.0/src/graylog_mcp/scan.py +556 -0
  50. graylog_mcp-0.3.0/src/graylog_mcp/scanrules.py +93 -0
  51. graylog_mcp-0.3.0/src/graylog_mcp/secrets.py +96 -0
  52. graylog_mcp-0.3.0/src/graylog_mcp/server.py +221 -0
  53. graylog_mcp-0.3.0/src/graylog_mcp/setup/__init__.py +1 -0
  54. graylog_mcp-0.3.0/src/graylog_mcp/setup/clients.py +486 -0
  55. graylog_mcp-0.3.0/src/graylog_mcp/setup/configfile.py +502 -0
  56. graylog_mcp-0.3.0/src/graylog_mcp/setup/connect.py +55 -0
  57. graylog_mcp-0.3.0/src/graylog_mcp/setup/detect.py +220 -0
  58. graylog_mcp-0.3.0/src/graylog_mcp/setup/doctor.py +187 -0
  59. graylog_mcp-0.3.0/src/graylog_mcp/setup/remote.py +130 -0
  60. graylog_mcp-0.3.0/src/graylog_mcp/setup/service.py +642 -0
  61. graylog_mcp-0.3.0/src/graylog_mcp/setup/tomlwrite.py +69 -0
  62. graylog_mcp-0.3.0/src/graylog_mcp/setup/wizard.py +303 -0
  63. graylog_mcp-0.3.0/src/graylog_mcp/shaping.py +309 -0
  64. graylog_mcp-0.3.0/src/graylog_mcp/shared.py +208 -0
  65. graylog_mcp-0.3.0/src/graylog_mcp/timerange.py +215 -0
  66. graylog_mcp-0.3.0/src/graylog_mcp/tools.py +1198 -0
  67. graylog_mcp-0.3.0/src/graylog_mcp/toolspecs/__init__.py +23 -0
  68. graylog_mcp-0.3.0/src/graylog_mcp/toolspecs/discovery.py +62 -0
  69. graylog_mcp-0.3.0/src/graylog_mcp/toolspecs/investigate.py +120 -0
  70. graylog_mcp-0.3.0/src/graylog_mcp/toolspecs/scanning.py +74 -0
  71. graylog_mcp-0.3.0/src/graylog_mcp/toolspecs/search.py +145 -0
  72. graylog_mcp-0.3.0/src/graylog_mcp/toolspecs/spec.py +117 -0
  73. graylog_mcp-0.3.0/src/graylog_mcp/usage.py +220 -0
  74. graylog_mcp-0.3.0/tests/__init__.py +0 -0
  75. graylog_mcp-0.3.0/tests/conftest.py +78 -0
  76. graylog_mcp-0.3.0/tests/fake_claude.py +69 -0
  77. graylog_mcp-0.3.0/tests/fake_graylog.py +800 -0
  78. graylog_mcp-0.3.0/tests/fake_jev.py +56 -0
  79. graylog_mcp-0.3.0/tests/integration/docker-compose.yml +112 -0
  80. graylog_mcp-0.3.0/tests/integration/run.sh +16 -0
  81. graylog_mcp-0.3.0/tests/integration/seed.py +171 -0
  82. graylog_mcp-0.3.0/tests/integration/test_integration.py +204 -0
  83. graylog_mcp-0.3.0/tests/smoke_repo.py +181 -0
  84. graylog_mcp-0.3.0/tests/smoke_start.py +95 -0
  85. graylog_mcp-0.3.0/tests/snapshots/model_surface.json +1191 -0
  86. graylog_mcp-0.3.0/tests/test_ask.py +227 -0
  87. graylog_mcp-0.3.0/tests/test_cli.py +110 -0
  88. graylog_mcp-0.3.0/tests/test_client.py +120 -0
  89. graylog_mcp-0.3.0/tests/test_config.py +170 -0
  90. graylog_mcp-0.3.0/tests/test_config_writes.py +189 -0
  91. graylog_mcp-0.3.0/tests/test_connect.py +150 -0
  92. graylog_mcp-0.3.0/tests/test_focus.py +183 -0
  93. graylog_mcp-0.3.0/tests/test_groups.py +195 -0
  94. graylog_mcp-0.3.0/tests/test_http.py +35 -0
  95. graylog_mcp-0.3.0/tests/test_model_surface.py +26 -0
  96. graylog_mcp-0.3.0/tests/test_operations.py +65 -0
  97. graylog_mcp-0.3.0/tests/test_output_budget.py +265 -0
  98. graylog_mcp-0.3.0/tests/test_permissions_textfields.py +70 -0
  99. graylog_mcp-0.3.0/tests/test_rca.py +241 -0
  100. graylog_mcp-0.3.0/tests/test_recover.py +238 -0
  101. graylog_mcp-0.3.0/tests/test_redact.py +491 -0
  102. graylog_mcp-0.3.0/tests/test_regressions.py +191 -0
  103. graylog_mcp-0.3.0/tests/test_remote.py +101 -0
  104. graylog_mcp-0.3.0/tests/test_repos.py +309 -0
  105. graylog_mcp-0.3.0/tests/test_scan.py +184 -0
  106. graylog_mcp-0.3.0/tests/test_scan_scenarios.py +269 -0
  107. graylog_mcp-0.3.0/tests/test_service.py +718 -0
  108. graylog_mcp-0.3.0/tests/test_setup.py +524 -0
  109. graylog_mcp-0.3.0/tests/test_shaping.py +75 -0
  110. graylog_mcp-0.3.0/tests/test_shared.py +254 -0
  111. graylog_mcp-0.3.0/tests/test_stacktrace.py +132 -0
  112. graylog_mcp-0.3.0/tests/test_timerange.py +75 -0
  113. graylog_mcp-0.3.0/tests/test_tools_contract.py +375 -0
  114. graylog_mcp-0.3.0/tests/test_toolspecs.py +157 -0
  115. graylog_mcp-0.3.0/tests/test_usage.py +169 -0
  116. graylog_mcp-0.3.0/uv.lock +1364 -0
@@ -0,0 +1,8 @@
1
+ .git
2
+ .venv
3
+ **/__pycache__
4
+ .pytest_cache
5
+ .ruff_cache
6
+ dist
7
+ tests
8
+ examples
@@ -0,0 +1,12 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = space
9
+ indent_size = 4
10
+
11
+ [*.{yml,yaml,toml,json,md}]
12
+ indent_size = 2
@@ -0,0 +1 @@
1
+ * @ntbang0901
@@ -0,0 +1,34 @@
1
+ name: Bug report
2
+ description: Something does not work as documented
3
+ labels: [bug]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: "Do not paste log lines, tokens or URLs of private Graylog instances. For security problems (for example a value that should be masked but is not), see SECURITY.md instead."
8
+ - type: input
9
+ id: version
10
+ attributes:
11
+ label: graylog-mcp version
12
+ placeholder: "graylog-mcp --version"
13
+ validations:
14
+ required: true
15
+ - type: input
16
+ id: graylog
17
+ attributes:
18
+ label: Graylog version and search backend
19
+ placeholder: "6.1.16 with OpenSearch 2.15 (shown by list_instances)"
20
+ validations:
21
+ required: true
22
+ - type: textarea
23
+ id: what
24
+ attributes:
25
+ label: What happened and what you expected
26
+ description: The tool called, its arguments and the (redacted) result or error.
27
+ validations:
28
+ required: true
29
+ - type: textarea
30
+ id: config
31
+ attributes:
32
+ label: Relevant configuration
33
+ description: Remove secrets and hostnames.
34
+ render: toml
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security problem
4
+ url: https://github.com/ntbang0901/graylog-mcp/security/advisories/new
5
+ about: Report a vulnerability or a masking gap privately.
@@ -0,0 +1,16 @@
1
+ name: Feature request
2
+ description: A tool, option or behaviour you would like
3
+ labels: [enhancement]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: Problem
9
+ description: What are you trying to investigate or automate, and what gets in the way today?
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: Proposal
16
+ description: How it could work. Note that the server must stay read-only towards Graylog.
@@ -0,0 +1,23 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: uv
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ groups:
8
+ python:
9
+ patterns: ["*"]
10
+ open-pull-requests-limit: 5
11
+
12
+ - package-ecosystem: github-actions
13
+ directory: /
14
+ schedule:
15
+ interval: weekly
16
+ groups:
17
+ actions:
18
+ patterns: ["*"]
19
+
20
+ - package-ecosystem: docker
21
+ directory: /
22
+ schedule:
23
+ interval: weekly
@@ -0,0 +1,15 @@
1
+ ## What and why
2
+
3
+ <!-- What does this change and which problem does it solve? Link the issue if there is one. -->
4
+
5
+ ## How it was tested
6
+
7
+ - [ ] `uv run pytest` (unit + contract)
8
+ - [ ] `uv run ruff check && uv run ruff format --check && uv run mypy`
9
+ - [ ] Integration against real Graylog (`tests/integration/run.sh <v43|v50|v52|v61|v70>`) — needed for changes to `backends/`, `client.py` or query building
10
+
11
+ ## Checklist
12
+
13
+ - [ ] The server still never writes to Graylog (only GET, and POST to the read-only search endpoints)
14
+ - [ ] New output goes through redaction and the size budget
15
+ - [ ] CHANGELOG.md updated under "Unreleased"
@@ -0,0 +1,156 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["**"]
6
+ tags-ignore: ["**"]
7
+ pull_request:
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: ci-${{ github.event.pull_request.number || github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ env:
18
+ FORCE_COLOR: "1"
19
+ UV_LOCKED: "1" # every uv command must match uv.lock
20
+
21
+ # Branch pushes run CI directly; pull requests run only when they come from a fork (a same-repo
22
+ # PR is already covered by the push of its branch). Each job repeats this condition.
23
+
24
+ jobs:
25
+ lint:
26
+ name: Lint and type check
27
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
28
+ runs-on: ubuntu-latest
29
+ timeout-minutes: 10
30
+ steps:
31
+ - uses: actions/checkout@v7
32
+ - uses: astral-sh/setup-uv@v7
33
+ with:
34
+ python-version: "3.13"
35
+ enable-cache: true
36
+ - run: uv sync
37
+ - name: Lockfile up to date
38
+ run: uv lock --check
39
+ - name: Ruff lint
40
+ run: uv run ruff check --output-format=github
41
+ - name: Ruff format
42
+ run: uv run ruff format --check
43
+ - name: Mypy
44
+ run: uv run mypy
45
+
46
+ test:
47
+ name: Test (${{ matrix.os }}, py${{ matrix.python }})
48
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
49
+ runs-on: ${{ matrix.os }}
50
+ timeout-minutes: 15
51
+ strategy:
52
+ fail-fast: false
53
+ matrix:
54
+ os: [ubuntu-latest]
55
+ python: ["3.11", "3.12", "3.13"]
56
+ include:
57
+ - { os: windows-latest, python: "3.13" }
58
+ - { os: macos-latest, python: "3.13" }
59
+ steps:
60
+ - uses: actions/checkout@v7
61
+ - uses: astral-sh/setup-uv@v7
62
+ with:
63
+ python-version: ${{ matrix.python }}
64
+ enable-cache: true
65
+ - run: uv sync
66
+ - name: Unit and contract tests
67
+ run: uv run pytest -q --cov --cov-report=term --cov-report=xml
68
+ - uses: actions/upload-artifact@v7
69
+ if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
70
+ with:
71
+ name: coverage
72
+ path: coverage.xml
73
+
74
+ build:
75
+ name: Build and check distribution
76
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
77
+ runs-on: ubuntu-latest
78
+ timeout-minutes: 10
79
+ steps:
80
+ - uses: actions/checkout@v7
81
+ - uses: astral-sh/setup-uv@v7
82
+ with:
83
+ python-version: "3.13"
84
+ - run: uv build
85
+ - name: Check metadata
86
+ run: uvx twine check --strict dist/*
87
+ - name: Smoke test the wheel in a clean environment
88
+ run: |
89
+ uv venv /tmp/smoke
90
+ uv pip install --python /tmp/smoke dist/*.whl
91
+ /tmp/smoke/bin/graylog-mcp --version
92
+ # --check exits 1 when an instance is unreachable, 2 on a configuration error
93
+ set +e
94
+ GRAYLOG_URL=http://127.0.0.1:9 GRAYLOG_TOKEN=x /tmp/smoke/bin/graylog-mcp --check > /dev/null
95
+ test $? -eq 1
96
+ - uses: actions/upload-artifact@v7
97
+ with:
98
+ name: dist
99
+ path: dist/
100
+
101
+ docker:
102
+ name: Docker image
103
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
104
+ runs-on: ubuntu-latest
105
+ timeout-minutes: 15
106
+ steps:
107
+ - uses: actions/checkout@v7
108
+ - uses: docker/setup-buildx-action@v4
109
+ - uses: docker/build-push-action@v7
110
+ with:
111
+ context: .
112
+ load: true
113
+ tags: graylog-mcp:ci
114
+ cache-from: type=gha
115
+ cache-to: type=gha,mode=max
116
+ - name: Smoke test the image
117
+ run: |
118
+ docker run --rm --entrypoint graylog-mcp graylog-mcp:ci --version
119
+ docker run -d --name mcp -p 8000:8000 -e GRAYLOG_URL=http://127.0.0.1:9 -e GRAYLOG_TOKEN=x \
120
+ -e GRAYLOG_MCP_HTTP_TOKEN=ci graylog-mcp:ci
121
+ for i in $(seq 1 30); do curl -fs localhost:8000/healthz && break; sleep 1; done
122
+ test "$(curl -s -o /dev/null -w '%{http_code}' -X POST localhost:8000/mcp)" = 401
123
+ docker rm -f mcp
124
+
125
+ start:
126
+ name: Start at login (${{ matrix.os }})
127
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
128
+ runs-on: ${{ matrix.os }}
129
+ timeout-minutes: 15
130
+ strategy:
131
+ fail-fast: false
132
+ matrix:
133
+ os: [ubuntu-latest, windows-latest, macos-latest]
134
+ steps:
135
+ - uses: actions/checkout@v7
136
+ - uses: astral-sh/setup-uv@v7
137
+ with:
138
+ python-version: "3.13"
139
+ - run: uv sync
140
+ - name: start, crash, restart, stop with the real service manager
141
+ env:
142
+ XDG_CURRENT_DESKTOP: CI # Linux without a systemd user session: XDG autostart
143
+ run: uv run python tests/smoke_start.py
144
+ - name: per-repository setup, port change and update on this machine
145
+ run: uv run python tests/smoke_repo.py
146
+
147
+ # Single required check for branch protection.
148
+ ci-ok:
149
+ name: CI passed
150
+ if: always()
151
+ needs: [lint, test, build, docker, start]
152
+ runs-on: ubuntu-latest
153
+ steps:
154
+ - env:
155
+ NEEDS: ${{ toJSON(needs) }}
156
+ run: echo "$NEEDS" | jq -e 'all(.[]; .result == "success" or .result == "skipped")'
@@ -0,0 +1,48 @@
1
+ name: Claude
2
+
3
+ # Claude implements an issue when a maintainer adds the "claude" label, and answers "@claude" in issues and pull
4
+ # requests. It works on a claude/ branch and opens a pull request for review; it never pushes to main.
5
+ # Only people with write access trigger it: labels need triage access, and the action ignores comments from others.
6
+ # Auth: the repository secret ANTHROPIC_API_KEY.
7
+
8
+ on:
9
+ issues:
10
+ types: [labeled]
11
+ issue_comment:
12
+ types: [created]
13
+ pull_request_review_comment:
14
+ types: [created]
15
+ pull_request_review:
16
+ types: [submitted]
17
+
18
+ permissions: {}
19
+
20
+ jobs:
21
+ claude:
22
+ if: >-
23
+ (github.event_name == 'issues' && github.event.label.name == 'claude') ||
24
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
25
+ (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
26
+ (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude'))
27
+ runs-on: ubuntu-latest
28
+ timeout-minutes: 30
29
+ concurrency:
30
+ group: claude-${{ github.event.issue.number || github.event.pull_request.number }}
31
+ permissions:
32
+ contents: write
33
+ pull-requests: write
34
+ issues: write
35
+ id-token: write
36
+ actions: read
37
+ steps:
38
+ - uses: actions/checkout@v7
39
+ with:
40
+ fetch-depth: 1
41
+ - uses: astral-sh/setup-uv@v7
42
+ - uses: anthropics/claude-code-action@v1
43
+ with:
44
+ anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
45
+ label_trigger: claude
46
+ claude_args: >-
47
+ --max-turns 60
48
+ --allowedTools "Bash(uv sync:*),Bash(uv run pytest:*),Bash(uv run ruff:*),Bash(uv run mypy:*),Bash(git:*)"
@@ -0,0 +1,34 @@
1
+ name: CodeQL
2
+
3
+ on:
4
+ push:
5
+ branches: [main, "claude/**"]
6
+ pull_request:
7
+ schedule:
8
+ - cron: "23 4 * * 2"
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ analyze:
15
+ name: Analyze (python, actions)
16
+ runs-on: ubuntu-latest
17
+ timeout-minutes: 20
18
+ permissions:
19
+ security-events: write
20
+ actions: read
21
+ contents: read
22
+ strategy:
23
+ fail-fast: false
24
+ matrix:
25
+ language: [python, actions]
26
+ steps:
27
+ - uses: actions/checkout@v7
28
+ - uses: github/codeql-action/init@v4
29
+ with:
30
+ languages: ${{ matrix.language }}
31
+ queries: security-extended
32
+ - uses: github/codeql-action/analyze@v4
33
+ with:
34
+ category: /language:${{ matrix.language }}
@@ -0,0 +1,53 @@
1
+ name: Integration (Graylog matrix)
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ schedule:
6
+ - cron: "17 3 * * 1" # weekly
7
+ pull_request:
8
+ paths:
9
+ - "src/graylog_mcp/backends/**"
10
+ - "src/graylog_mcp/client.py"
11
+ - "src/graylog_mcp/rca.py"
12
+ - "src/graylog_mcp/scan.py"
13
+ - "src/graylog_mcp/scanrules.py"
14
+ - "tests/fake_graylog.py"
15
+ - "tests/integration/**"
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ concurrency:
21
+ group: integration-${{ github.event.pull_request.number || github.ref }}
22
+ cancel-in-progress: true
23
+
24
+ jobs:
25
+ graylog:
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 30
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ include:
32
+ - { profile: v43, port: 9043 }
33
+ - { profile: v50, port: 9050 }
34
+ - { profile: v52, port: 9052 }
35
+ - { profile: v61, port: 9061 }
36
+ - { profile: v70, port: 9070 }
37
+ env:
38
+ GRAYLOG_IT_URL: http://127.0.0.1:${{ matrix.port }}
39
+ steps:
40
+ - uses: actions/checkout@v7
41
+ - uses: astral-sh/setup-uv@v7
42
+ with:
43
+ python-version: "3.13"
44
+ enable-cache: true
45
+ - run: uv sync --locked
46
+ - run: sudo sysctl -w vm.max_map_count=262144
47
+ - run: docker compose -f tests/integration/docker-compose.yml --profile ${{ matrix.profile }} up -d
48
+ - name: Seed Graylog
49
+ run: uv run python tests/integration/seed.py
50
+ - name: Run every tool against Graylog
51
+ run: uv run pytest -m integration -v -s tests/integration
52
+ - if: failure()
53
+ run: docker compose -f tests/integration/docker-compose.yml --profile ${{ matrix.profile }} logs --tail 200
@@ -0,0 +1,96 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v7
15
+ - uses: astral-sh/setup-uv@v7
16
+ - run: uv sync --locked && uv run pytest -q && uv run mypy
17
+ - name: Check tag matches version and changelog
18
+ run: |
19
+ version="${GITHUB_REF_NAME#v}"
20
+ test "$(uv run python -c 'import graylog_mcp; print(graylog_mcp.__version__)')" = "$version"
21
+ grep -q "^version = \"$version\"" pyproject.toml
22
+ grep -q "^## \[$version\]" CHANGELOG.md
23
+ - run: uv build
24
+ - uses: actions/upload-artifact@v7
25
+ with:
26
+ name: dist
27
+ path: dist/
28
+
29
+ publish-pypi:
30
+ needs: build
31
+ runs-on: ubuntu-latest
32
+ environment: pypi
33
+ permissions:
34
+ id-token: write # PyPI trusted publishing + PEP 740 attestations (signed with Sigstore)
35
+ steps:
36
+ - uses: actions/download-artifact@v8
37
+ with:
38
+ name: dist
39
+ path: dist/
40
+ - uses: pypa/gh-action-pypi-publish@release/v1
41
+ with:
42
+ attestations: true
43
+
44
+ github-release:
45
+ needs: build
46
+ runs-on: ubuntu-latest
47
+ permissions:
48
+ contents: write
49
+ id-token: write
50
+ steps:
51
+ - uses: actions/checkout@v7
52
+ - uses: actions/download-artifact@v8
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+ - name: Sign with Sigstore
57
+ uses: sigstore/gh-action-sigstore-python@v3.5.0
58
+ with:
59
+ inputs: ./dist/*.tar.gz ./dist/*.whl
60
+ - name: Create release
61
+ env:
62
+ GH_TOKEN: ${{ github.token }}
63
+ run: |
64
+ notes=$(awk "/^## \\[${GITHUB_REF_NAME#v}\\]/{f=1;next} /^## \\[/{f=0} f" CHANGELOG.md)
65
+ gh release create "$GITHUB_REF_NAME" dist/* --title "$GITHUB_REF_NAME" --notes "$notes"
66
+
67
+ docker:
68
+ needs: build
69
+ runs-on: ubuntu-latest
70
+ permissions:
71
+ contents: read
72
+ packages: write
73
+ id-token: write
74
+ steps:
75
+ - uses: actions/checkout@v7
76
+ - uses: docker/setup-buildx-action@v4
77
+ - uses: docker/login-action@v4
78
+ with:
79
+ registry: ghcr.io
80
+ username: ${{ github.actor }}
81
+ password: ${{ secrets.GITHUB_TOKEN }}
82
+ - uses: docker/metadata-action@v6
83
+ id: meta
84
+ with:
85
+ images: ghcr.io/${{ github.repository }}
86
+ tags: |
87
+ type=semver,pattern={{version}}
88
+ type=semver,pattern={{major}}.{{minor}}
89
+ - uses: docker/build-push-action@v7
90
+ id: push
91
+ with:
92
+ push: true
93
+ tags: ${{ steps.meta.outputs.tags }}
94
+ labels: ${{ steps.meta.outputs.labels }}
95
+ provenance: true
96
+ sbom: true
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+ .coverage
@@ -0,0 +1,26 @@
1
+ # pip install pre-commit && pre-commit install
2
+ repos:
3
+ - repo: https://github.com/pre-commit/pre-commit-hooks
4
+ rev: v5.0.0
5
+ hooks:
6
+ - id: check-toml
7
+ - id: check-yaml
8
+ - id: end-of-file-fixer
9
+ - id: trailing-whitespace
10
+ - id: check-merge-conflict
11
+ - id: detect-private-key
12
+ exclude: ^tests/
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.16.10
15
+ hooks:
16
+ - id: ruff-check
17
+ args: [--fix]
18
+ - id: ruff-format
19
+ - repo: local
20
+ hooks:
21
+ - id: mypy
22
+ name: mypy
23
+ entry: uv run mypy
24
+ language: system
25
+ pass_filenames: false
26
+ types: [python]