rootly-mcp-server 2.2.19__tar.gz → 2.2.20__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.
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.github/dependabot.yml +23 -7
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.github/workflows/ci.yml +12 -12
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.github/workflows/dependency-review.yml +1 -1
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.github/workflows/openapi-audit.yml +4 -4
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.github/workflows/pypi-release.yml +2 -2
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.gitignore +4 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/CHANGELOG.md +15 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/PKG-INFO +4 -2
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/pyproject.toml +4 -2
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/__main__.py +9 -5
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/code_mode.py +4 -6
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/server.py +8 -9
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/spec_transform.py +1 -2
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/tools/incidents.py +23 -32
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/tools/oncall.py +5 -2
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/transport.py +7 -9
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/integration/local/test_basic.py +3 -3
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/integration/local/test_smart_tools.py +21 -21
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/integration/remote/test_essential.py +6 -6
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_code_mode_module.py +6 -2
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_oncall_metrics.py +3 -3
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_server.py +36 -19
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_smart_utils.py +0 -1
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_transport_module.py +21 -7
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/uv.lock +27 -10
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.dockerignore +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.gitattributes +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.github/openapi-audit-remote-baseline.json +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.semaphore/deploy-staging.yml +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.semaphore/deploy.yml +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.semaphore/semaphore.yml +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/.semaphore/update-task-definition.sh +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/CONTRIBUTING.md +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/Dockerfile +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/LICENSE +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/README.md +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/examples/skills/README.md +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/gemini-extension.json +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/rootly-mcp-server-demo.gif +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/rootly_openapi.json +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/scripts/audit_openapi.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/scripts/setup-hooks.sh +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/server.json +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/__init__.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/client.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/data/__init__.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/exceptions.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/legacy_server.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/mcp_error.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/monitoring.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/och_client.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/pagination.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/payload_stripping.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/security.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/server_defaults.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/smart_utils.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/tools/__init__.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/tools/alerts.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/tools/resources.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/utils.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/validators.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/README.md +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/conftest.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/test_client.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_alert_stripping.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_authentication.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_exceptions.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_http_headers.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_legacy_server_module.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_main_transport.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_mcp_error_module.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_och_client.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_oncall_handoff.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_oncall_new_tools.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_oncall_severity_normalization.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_payload_stripping_module.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_security.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_server_defaults_module.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_tools.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_utils.py +0 -0
- {rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/tests/unit/test_validators.py +0 -0
|
@@ -1,35 +1,51 @@
|
|
|
1
1
|
version: 2
|
|
2
|
+
|
|
2
3
|
updates:
|
|
3
|
-
- package-ecosystem: "
|
|
4
|
+
- package-ecosystem: "pip"
|
|
4
5
|
directory: "/"
|
|
5
6
|
schedule:
|
|
6
7
|
interval: "weekly"
|
|
8
|
+
open-pull-requests-limit: 20
|
|
7
9
|
cooldown:
|
|
8
10
|
default-days: 3
|
|
11
|
+
semver-major-days: 7
|
|
9
12
|
groups:
|
|
10
|
-
|
|
13
|
+
minor-and-patch:
|
|
14
|
+
applies-to: version-updates
|
|
15
|
+
update-types:
|
|
16
|
+
- "minor"
|
|
17
|
+
- "patch"
|
|
11
18
|
patterns:
|
|
12
19
|
- "*"
|
|
13
20
|
|
|
14
|
-
- package-ecosystem: "
|
|
21
|
+
- package-ecosystem: "docker"
|
|
15
22
|
directory: "/"
|
|
16
23
|
schedule:
|
|
17
24
|
interval: "weekly"
|
|
18
|
-
open-pull-requests-limit:
|
|
25
|
+
open-pull-requests-limit: 20
|
|
19
26
|
cooldown:
|
|
20
27
|
default-days: 3
|
|
21
|
-
semver-major-days: 7
|
|
22
28
|
groups:
|
|
23
29
|
minor-and-patch:
|
|
24
30
|
applies-to: version-updates
|
|
25
31
|
update-types:
|
|
26
32
|
- "minor"
|
|
27
33
|
- "patch"
|
|
34
|
+
patterns:
|
|
35
|
+
- "*"
|
|
28
36
|
|
|
29
|
-
- package-ecosystem: "
|
|
37
|
+
- package-ecosystem: "github-actions"
|
|
30
38
|
directory: "/"
|
|
31
39
|
schedule:
|
|
32
40
|
interval: "weekly"
|
|
41
|
+
open-pull-requests-limit: 20
|
|
33
42
|
cooldown:
|
|
34
43
|
default-days: 3
|
|
35
|
-
|
|
44
|
+
groups:
|
|
45
|
+
minor-and-patch:
|
|
46
|
+
applies-to: version-updates
|
|
47
|
+
update-types:
|
|
48
|
+
- "minor"
|
|
49
|
+
- "patch"
|
|
50
|
+
patterns:
|
|
51
|
+
- "*"
|
|
@@ -19,10 +19,10 @@ jobs:
|
|
|
19
19
|
runs-on: ubuntu-latest
|
|
20
20
|
steps:
|
|
21
21
|
- name: Checkout code
|
|
22
|
-
uses: actions/checkout@v6
|
|
22
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
23
23
|
|
|
24
24
|
- name: Set up Python
|
|
25
|
-
uses: actions/setup-python@v6
|
|
25
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
26
26
|
with:
|
|
27
27
|
python-version: "3.12"
|
|
28
28
|
|
|
@@ -53,10 +53,10 @@ jobs:
|
|
|
53
53
|
|
|
54
54
|
steps:
|
|
55
55
|
- name: Checkout code
|
|
56
|
-
uses: actions/checkout@v6
|
|
56
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
57
57
|
|
|
58
58
|
- name: Set up Python ${{ matrix.python-version }}
|
|
59
|
-
uses: actions/setup-python@v6
|
|
59
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
60
60
|
with:
|
|
61
61
|
python-version: ${{ matrix.python-version }}
|
|
62
62
|
|
|
@@ -87,13 +87,13 @@ jobs:
|
|
|
87
87
|
name: Container Tests
|
|
88
88
|
runs-on: ubuntu-latest
|
|
89
89
|
needs: test
|
|
90
|
-
if: ${{
|
|
90
|
+
if: ${{ github.repository == 'Rootly-AI-Labs/rootly-mcp-server' }}
|
|
91
91
|
steps:
|
|
92
92
|
- name: Checkout code
|
|
93
|
-
uses: actions/checkout@v6
|
|
93
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
94
94
|
|
|
95
95
|
- name: Set up Python
|
|
96
|
-
uses: actions/setup-python@v6
|
|
96
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
97
97
|
with:
|
|
98
98
|
python-version: "3.12"
|
|
99
99
|
|
|
@@ -135,10 +135,10 @@ jobs:
|
|
|
135
135
|
runs-on: ubuntu-latest
|
|
136
136
|
steps:
|
|
137
137
|
- name: Checkout code
|
|
138
|
-
uses: actions/checkout@v6
|
|
138
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
139
139
|
|
|
140
140
|
- name: Set up Python
|
|
141
|
-
uses: actions/setup-python@v6
|
|
141
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
142
142
|
with:
|
|
143
143
|
python-version: "3.12"
|
|
144
144
|
|
|
@@ -161,10 +161,10 @@ jobs:
|
|
|
161
161
|
needs: [quality, test, security]
|
|
162
162
|
steps:
|
|
163
163
|
- name: Checkout code
|
|
164
|
-
uses: actions/checkout@v6
|
|
164
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
165
165
|
|
|
166
166
|
- name: Set up Python
|
|
167
|
-
uses: actions/setup-python@v6
|
|
167
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
168
168
|
with:
|
|
169
169
|
python-version: "3.12"
|
|
170
170
|
|
|
@@ -175,7 +175,7 @@ jobs:
|
|
|
175
175
|
run: uv build
|
|
176
176
|
|
|
177
177
|
- name: Upload build artifacts
|
|
178
|
-
uses: actions/upload-artifact@v7
|
|
178
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
179
179
|
with:
|
|
180
180
|
name: dist
|
|
181
181
|
path: dist/
|
|
@@ -36,10 +36,10 @@ jobs:
|
|
|
36
36
|
runs-on: ubuntu-latest
|
|
37
37
|
steps:
|
|
38
38
|
- name: Checkout code
|
|
39
|
-
uses: actions/checkout@v6
|
|
39
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
40
40
|
|
|
41
41
|
- name: Set up Python
|
|
42
|
-
uses: actions/setup-python@v6
|
|
42
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
43
43
|
with:
|
|
44
44
|
python-version: "3.12"
|
|
45
45
|
|
|
@@ -58,10 +58,10 @@ jobs:
|
|
|
58
58
|
if: ${{ github.event_name != 'pull_request' }}
|
|
59
59
|
steps:
|
|
60
60
|
- name: Checkout code
|
|
61
|
-
uses: actions/checkout@v6
|
|
61
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
62
62
|
|
|
63
63
|
- name: Set up Python
|
|
64
|
-
uses: actions/setup-python@v6
|
|
64
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
65
65
|
with:
|
|
66
66
|
python-version: "3.12"
|
|
67
67
|
|
|
@@ -13,10 +13,10 @@ jobs:
|
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
steps:
|
|
15
15
|
- name: Checkout code
|
|
16
|
-
uses: actions/checkout@v6
|
|
16
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
17
17
|
|
|
18
18
|
- name: Set up Python
|
|
19
|
-
uses: actions/setup-python@v6
|
|
19
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
20
20
|
with:
|
|
21
21
|
python-version: '3.12'
|
|
22
22
|
|
|
@@ -5,6 +5,21 @@ All notable changes to the Rootly MCP Server will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.2.20] - Released 2026-04-21
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
|
|
12
|
+
- **Critical Security Updates**: Upgraded vulnerable dependencies to address 3 security advisories
|
|
13
|
+
- **authlib**: Updated from `1.6.9` to `1.7.0` to fix CSRF protection vulnerability (GHSA-jj8c-mmj3-mmgv)
|
|
14
|
+
- **python-dotenv**: Updated from `1.1.0` to `1.2.2` to fix symlink attack vulnerability (GHSA-m8f7-34r5-grfg)
|
|
15
|
+
- **python-multipart**: Updated from `0.0.22` to `0.0.26` to fix denial of service vulnerability (CVE-2026-40347)
|
|
16
|
+
- **Dependabot Configuration**: Fixed unsupported `semver-major-days` property for docker and github-actions ecosystems
|
|
17
|
+
|
|
18
|
+
### Dependencies
|
|
19
|
+
|
|
20
|
+
- **joserfc**: Added `1.6.4` as new dependency (required by updated authlib)
|
|
21
|
+
- **Security Scanning**: All known vulnerabilities resolved as confirmed by pip-audit
|
|
22
|
+
|
|
8
23
|
## [2.2.19] - Released 2026-04-17
|
|
9
24
|
|
|
10
25
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rootly-mcp-server
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.20
|
|
4
4
|
Summary: Secure Model Context Protocol server for Rootly APIs with AI SRE capabilities, comprehensive error handling, and input validation
|
|
5
5
|
Project-URL: Homepage, https://github.com/Rootly-AI-Labs/Rootly-MCP-server
|
|
6
6
|
Project-URL: Issues, https://github.com/Rootly-AI-Labs/Rootly-MCP-server/issues
|
|
@@ -21,7 +21,7 @@ Classifier: Topic :: Software Development :: Build Tools
|
|
|
21
21
|
Classifier: Topic :: System :: Monitoring
|
|
22
22
|
Classifier: Topic :: System :: Systems Administration
|
|
23
23
|
Requires-Python: >=3.12
|
|
24
|
-
Requires-Dist: authlib>=1.6.
|
|
24
|
+
Requires-Dist: authlib>=1.6.11
|
|
25
25
|
Requires-Dist: brotli>=1.2.0
|
|
26
26
|
Requires-Dist: cryptography>=46.0.7
|
|
27
27
|
Requires-Dist: fastmcp[code-mode]==3.2.0
|
|
@@ -29,6 +29,8 @@ Requires-Dist: httpx==0.28.1
|
|
|
29
29
|
Requires-Dist: numpy>=1.24.0
|
|
30
30
|
Requires-Dist: pydantic==2.12.5
|
|
31
31
|
Requires-Dist: pygments>=2.20.0
|
|
32
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
33
|
+
Requires-Dist: python-multipart>=0.0.26
|
|
32
34
|
Requires-Dist: requests==2.33.1
|
|
33
35
|
Requires-Dist: scikit-learn>=1.3.0
|
|
34
36
|
Provides-Extra: dev
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "rootly-mcp-server"
|
|
3
|
-
version = "2.2.
|
|
3
|
+
version = "2.2.20"
|
|
4
4
|
description = "Secure Model Context Protocol server for Rootly APIs with AI SRE capabilities, comprehensive error handling, and input validation"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -37,7 +37,9 @@ classifiers = [
|
|
|
37
37
|
]
|
|
38
38
|
dependencies = [
|
|
39
39
|
"fastmcp[code-mode]==3.2.0", # Pin to patched version with Code Mode support
|
|
40
|
-
"authlib>=1.6.
|
|
40
|
+
"authlib>=1.6.11", # Override vulnerable transitive authlib (GHSA-jj8c-mmj3-mmgv)
|
|
41
|
+
"python-dotenv>=1.2.2", # Override vulnerable transitive python-dotenv (GHSA-m8f7-34r5-grfg)
|
|
42
|
+
"python-multipart>=0.0.26", # Override vulnerable transitive python-multipart (CVE-2026-40347)
|
|
41
43
|
"cryptography>=46.0.7", # Override vulnerable transitive cryptography
|
|
42
44
|
"Pygments>=2.20.0", # Override vulnerable transitive Pygments
|
|
43
45
|
"requests==2.33.1", # Pin to patched version
|
|
@@ -50,9 +50,7 @@ def normalize_transport(value: str) -> TransportName:
|
|
|
50
50
|
return mapped
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def normalize_transport_or_default(
|
|
54
|
-
value: str, default: TransportName = "stdio"
|
|
55
|
-
) -> TransportName:
|
|
53
|
+
def normalize_transport_or_default(value: str, default: TransportName = "stdio") -> TransportName:
|
|
56
54
|
"""Normalize transport value, falling back to default when invalid."""
|
|
57
55
|
try:
|
|
58
56
|
return normalize_transport(value)
|
|
@@ -275,7 +273,9 @@ def run_dual_http_server(
|
|
|
275
273
|
stateless=stateless_http,
|
|
276
274
|
)
|
|
277
275
|
code_mode_http_app = StreamableHTTPASGIApp(code_mode_session_manager)
|
|
278
|
-
routes.append(
|
|
276
|
+
routes.append(
|
|
277
|
+
Route(code_mode_path, endpoint=code_mode_http_app, methods=streamable_methods)
|
|
278
|
+
)
|
|
279
279
|
|
|
280
280
|
routes.extend(server._get_additional_http_routes()) # noqa: SLF001
|
|
281
281
|
|
|
@@ -373,7 +373,11 @@ def main():
|
|
|
373
373
|
# argparse already normalizes/validates --transport via type=normalize_transport
|
|
374
374
|
normalized_transport = args.transport
|
|
375
375
|
code_mode_enabled = args.enable_code_mode or code_mode_enabled_from_env(default=True)
|
|
376
|
-
code_mode_path =
|
|
376
|
+
code_mode_path = (
|
|
377
|
+
normalize_code_mode_path(args.code_mode_path)
|
|
378
|
+
if args.code_mode_path
|
|
379
|
+
else code_mode_path_from_env()
|
|
380
|
+
)
|
|
377
381
|
server = create_rootly_mcp_server(
|
|
378
382
|
swagger_path=args.swagger_path,
|
|
379
383
|
name=args.name,
|
|
@@ -172,8 +172,7 @@ class CompatibleMontySandboxProvider(MontySandboxProvider):
|
|
|
172
172
|
|
|
173
173
|
inputs = inputs or {}
|
|
174
174
|
async_functions = {
|
|
175
|
-
key: _ensure_async(value)
|
|
176
|
-
for key, value in (external_functions or {}).items()
|
|
175
|
+
key: _ensure_async(value) for key, value in (external_functions or {}).items()
|
|
177
176
|
}
|
|
178
177
|
|
|
179
178
|
monty = self._build_monty_runner(
|
|
@@ -219,10 +218,9 @@ class RootlyCodeMode(CodeMode):
|
|
|
219
218
|
async def call_tool(tool_name: str, params: dict[str, Any]) -> Any:
|
|
220
219
|
resolved_name = _normalize_execute_tool_name(tool_name)
|
|
221
220
|
backend_tools = await _get_cached_tools()
|
|
222
|
-
tool = (
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
)
|
|
221
|
+
tool = transform._find_tool(
|
|
222
|
+
resolved_name, transform._build_discovery_tools()
|
|
223
|
+
) or transform._find_tool(resolved_name, backend_tools)
|
|
226
224
|
if tool is None:
|
|
227
225
|
raise NotFoundError(f"Unknown tool: {tool_name}")
|
|
228
226
|
|
|
@@ -201,16 +201,14 @@ def _log_tool_usage_event(
|
|
|
201
201
|
event["error_type"] = error_type
|
|
202
202
|
if error_context:
|
|
203
203
|
event.update(
|
|
204
|
-
{
|
|
205
|
-
key: value
|
|
206
|
-
for key, value in error_context.items()
|
|
207
|
-
if value not in ("", [], None, {})
|
|
208
|
-
}
|
|
204
|
+
{key: value for key, value in error_context.items() if value not in ("", [], None, {})}
|
|
209
205
|
)
|
|
210
206
|
|
|
211
207
|
_configure_tool_usage_json_logger()
|
|
212
208
|
_tool_usage_json_logger.info(
|
|
213
|
-
json.dumps(
|
|
209
|
+
json.dumps(
|
|
210
|
+
{k: v for k, v in event.items() if v not in ("", [], None)}, separators=(",", ":")
|
|
211
|
+
)
|
|
214
212
|
)
|
|
215
213
|
|
|
216
214
|
|
|
@@ -405,8 +403,7 @@ def create_rootly_mcp_server(
|
|
|
405
403
|
f"/v1{path}" if not path.startswith("/v1") else path for path in allowed_paths
|
|
406
404
|
]
|
|
407
405
|
delete_allowed_paths_v1 = [
|
|
408
|
-
f"/v1{path}" if not path.startswith("/v1") else path
|
|
409
|
-
for path in delete_allowed_paths
|
|
406
|
+
f"/v1{path}" if not path.startswith("/v1") else path for path in delete_allowed_paths
|
|
410
407
|
]
|
|
411
408
|
|
|
412
409
|
logger.info(f"Creating Rootly MCP Server with allowed paths: {allowed_paths_v1}")
|
|
@@ -518,7 +515,9 @@ def create_rootly_mcp_server(
|
|
|
518
515
|
logger.debug(
|
|
519
516
|
f"make_authenticated_request: client_ip={client_ip}, headers_keys={list(request_headers.keys()) if request_headers else []}"
|
|
520
517
|
)
|
|
521
|
-
direct_auth_header =
|
|
518
|
+
direct_auth_header = (
|
|
519
|
+
request_headers.get("authorization", "") if request_headers else ""
|
|
520
|
+
)
|
|
522
521
|
effective_auth_header = direct_auth_header or _session_auth_token.get("")
|
|
523
522
|
if direct_auth_header:
|
|
524
523
|
logger.debug("make_authenticated_request: Found auth header, adding to request")
|
{rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/spec_transform.py
RENAMED
|
@@ -164,8 +164,7 @@ def _filter_openapi_spec(
|
|
|
164
164
|
if not allow_delete:
|
|
165
165
|
path_item.pop("delete", None)
|
|
166
166
|
if not any(
|
|
167
|
-
method.lower() in ["get", "post", "put", "patch", "delete"]
|
|
168
|
-
for method in path_item
|
|
167
|
+
method.lower() in ["get", "post", "put", "patch", "delete"] for method in path_item
|
|
169
168
|
):
|
|
170
169
|
paths_to_remove.append(path)
|
|
171
170
|
for path in paths_to_remove:
|
{rootly_mcp_server-2.2.19 → rootly_mcp_server-2.2.20}/src/rootly_mcp_server/tools/incidents.py
RENAMED
|
@@ -119,10 +119,13 @@ def register_incident_tools(
|
|
|
119
119
|
|
|
120
120
|
for candidate in response.json().get("data", []):
|
|
121
121
|
attrs = candidate.get("attributes", {})
|
|
122
|
-
candidate_value =
|
|
123
|
-
"name"
|
|
122
|
+
candidate_value = (
|
|
123
|
+
attrs.get("slug") if filter_key == "filter[slug]" else attrs.get("name")
|
|
124
124
|
)
|
|
125
|
-
if
|
|
125
|
+
if (
|
|
126
|
+
isinstance(candidate_value, str)
|
|
127
|
+
and candidate_value.lower() == expected_value.lower()
|
|
128
|
+
):
|
|
126
129
|
matched_id = str(candidate.get("id"))
|
|
127
130
|
break
|
|
128
131
|
|
|
@@ -137,8 +140,7 @@ def register_incident_tools(
|
|
|
137
140
|
|
|
138
141
|
if unresolved_teams:
|
|
139
142
|
raise ValueError(
|
|
140
|
-
"Could not resolve team names/slugs to team IDs: "
|
|
141
|
-
+ ", ".join(unresolved_teams)
|
|
143
|
+
"Could not resolve team names/slugs to team IDs: " + ", ".join(unresolved_teams)
|
|
142
144
|
)
|
|
143
145
|
|
|
144
146
|
return ",".join(dict.fromkeys(resolved_team_ids)), resolved_team_lookup
|
|
@@ -214,9 +216,7 @@ def register_incident_tools(
|
|
|
214
216
|
async def list_incidents(
|
|
215
217
|
query: Annotated[
|
|
216
218
|
str,
|
|
217
|
-
Field(
|
|
218
|
-
description="Optional free-text search across incident titles and summaries"
|
|
219
|
-
),
|
|
219
|
+
Field(description="Optional free-text search across incident titles and summaries"),
|
|
220
220
|
] = "",
|
|
221
221
|
teams: Annotated[
|
|
222
222
|
str,
|
|
@@ -238,9 +238,7 @@ def register_incident_tools(
|
|
|
238
238
|
] = "",
|
|
239
239
|
severity: Annotated[
|
|
240
240
|
str,
|
|
241
|
-
Field(
|
|
242
|
-
description="Optional severity filter (e.g., critical, high, medium, low)"
|
|
243
|
-
),
|
|
241
|
+
Field(description="Optional severity filter (e.g., critical, high, medium, low)"),
|
|
244
242
|
] = "",
|
|
245
243
|
status: Annotated[
|
|
246
244
|
str,
|
|
@@ -250,15 +248,11 @@ def register_incident_tools(
|
|
|
250
248
|
] = "",
|
|
251
249
|
started_after: Annotated[
|
|
252
250
|
str,
|
|
253
|
-
Field(
|
|
254
|
-
description="Filter incidents that started at or after this ISO 8601 timestamp"
|
|
255
|
-
),
|
|
251
|
+
Field(description="Filter incidents that started at or after this ISO 8601 timestamp"),
|
|
256
252
|
] = "",
|
|
257
253
|
started_before: Annotated[
|
|
258
254
|
str,
|
|
259
|
-
Field(
|
|
260
|
-
description="Filter incidents that started at or before this ISO 8601 timestamp"
|
|
261
|
-
),
|
|
255
|
+
Field(description="Filter incidents that started at or before this ISO 8601 timestamp"),
|
|
262
256
|
] = "",
|
|
263
257
|
custom_field_selected_option_ids: Annotated[
|
|
264
258
|
str,
|
|
@@ -340,9 +334,7 @@ def register_incident_tools(
|
|
|
340
334
|
async def collect_incidents(
|
|
341
335
|
query: Annotated[
|
|
342
336
|
str,
|
|
343
|
-
Field(
|
|
344
|
-
description="Optional free-text search across incident titles and summaries"
|
|
345
|
-
),
|
|
337
|
+
Field(description="Optional free-text search across incident titles and summaries"),
|
|
346
338
|
] = "",
|
|
347
339
|
teams: Annotated[
|
|
348
340
|
str,
|
|
@@ -364,9 +356,7 @@ def register_incident_tools(
|
|
|
364
356
|
] = "",
|
|
365
357
|
severity: Annotated[
|
|
366
358
|
str,
|
|
367
|
-
Field(
|
|
368
|
-
description="Optional severity filter (e.g., critical, high, medium, low)"
|
|
369
|
-
),
|
|
359
|
+
Field(description="Optional severity filter (e.g., critical, high, medium, low)"),
|
|
370
360
|
] = "",
|
|
371
361
|
status: Annotated[
|
|
372
362
|
str,
|
|
@@ -376,15 +366,11 @@ def register_incident_tools(
|
|
|
376
366
|
] = "",
|
|
377
367
|
started_after: Annotated[
|
|
378
368
|
str,
|
|
379
|
-
Field(
|
|
380
|
-
description="Filter incidents that started at or after this ISO 8601 timestamp"
|
|
381
|
-
),
|
|
369
|
+
Field(description="Filter incidents that started at or after this ISO 8601 timestamp"),
|
|
382
370
|
] = "",
|
|
383
371
|
started_before: Annotated[
|
|
384
372
|
str,
|
|
385
|
-
Field(
|
|
386
|
-
description="Filter incidents that started at or before this ISO 8601 timestamp"
|
|
387
|
-
),
|
|
373
|
+
Field(description="Filter incidents that started at or before this ISO 8601 timestamp"),
|
|
388
374
|
] = "",
|
|
389
375
|
custom_field_selected_option_ids: Annotated[
|
|
390
376
|
str,
|
|
@@ -452,7 +438,9 @@ def register_incident_tools(
|
|
|
452
438
|
page_params["page[size]"] = batch_size
|
|
453
439
|
page_params["page[number]"] = page_number
|
|
454
440
|
|
|
455
|
-
response = await make_authenticated_request(
|
|
441
|
+
response = await make_authenticated_request(
|
|
442
|
+
"GET", "/v1/incidents", params=page_params
|
|
443
|
+
)
|
|
456
444
|
response.raise_for_status()
|
|
457
445
|
|
|
458
446
|
response_data = strip_heavy_nested_data(response.json())
|
|
@@ -483,7 +471,9 @@ def register_incident_tools(
|
|
|
483
471
|
results_truncated = True
|
|
484
472
|
|
|
485
473
|
return {
|
|
486
|
-
"incidents": [
|
|
474
|
+
"incidents": [
|
|
475
|
+
_summarize_incident_record(incident) for incident in collected_incidents
|
|
476
|
+
],
|
|
487
477
|
"returned_incidents": len(collected_incidents),
|
|
488
478
|
"collection": {
|
|
489
479
|
"max_results": max_results,
|
|
@@ -861,7 +851,8 @@ def register_incident_tools(
|
|
|
861
851
|
return cast(
|
|
862
852
|
JsonDict,
|
|
863
853
|
mcp_error.tool_error(
|
|
864
|
-
|
|
854
|
+
"Must provide either incident_id or incident_description",
|
|
855
|
+
"validation_error",
|
|
865
856
|
),
|
|
866
857
|
)
|
|
867
858
|
|
|
@@ -36,7 +36,7 @@ def _truncate_text(value: Any, max_length: int = 280) -> str | None:
|
|
|
36
36
|
return None
|
|
37
37
|
|
|
38
38
|
if len(value) <= max_length:
|
|
39
|
-
return value
|
|
39
|
+
return str(value)
|
|
40
40
|
|
|
41
41
|
return f"{value[: max_length - 1].rstrip()}…"
|
|
42
42
|
|
|
@@ -100,6 +100,7 @@ def register_oncall_tools(
|
|
|
100
100
|
mcp_error: MCPErrorLike,
|
|
101
101
|
) -> None:
|
|
102
102
|
"""Register on-call analysis and scheduling tools on the MCP server."""
|
|
103
|
+
|
|
103
104
|
@mcp.tool()
|
|
104
105
|
async def get_oncall_shift_metrics(
|
|
105
106
|
start_date: Annotated[
|
|
@@ -1229,7 +1230,9 @@ def register_oncall_tools(
|
|
|
1229
1230
|
_lookup_maps_lock = asyncio.Lock()
|
|
1230
1231
|
|
|
1231
1232
|
# Helper function to fetch users and schedules for enrichment
|
|
1232
|
-
async def _fetch_users_and_schedules_maps() -> tuple[
|
|
1233
|
+
async def _fetch_users_and_schedules_maps() -> tuple[
|
|
1234
|
+
dict[str, Any], dict[str, Any], dict[str, Any]
|
|
1235
|
+
]:
|
|
1233
1236
|
"""Fetch all users, schedules, and teams to build lookup maps.
|
|
1234
1237
|
|
|
1235
1238
|
Results are cached for 5 minutes to avoid repeated API calls.
|
|
@@ -49,7 +49,7 @@ def _sanitize_log_excerpt(value: Any, max_length: int = _MAX_LOG_EXCERPT_CHARS)
|
|
|
49
49
|
|
|
50
50
|
text = str(value).replace("\r\n", "\n").strip()
|
|
51
51
|
text = re.sub(r"/[\w/.-]+\.py", "[file]", text)
|
|
52
|
-
text = re.sub(r
|
|
52
|
+
text = re.sub(r"C:\\[\w\\.-]+\.py", "[file]", text)
|
|
53
53
|
text = re.sub(r'File "[^"]+"', 'File "[file]"', text)
|
|
54
54
|
text = re.sub(r"Bearer\s+[A-Za-z0-9._~\-]+", "Bearer ***REDACTED***", text, flags=re.I)
|
|
55
55
|
text = re.sub(r"\brootly_[A-Za-z0-9]+\b", "rootly_***REDACTED***", text)
|
|
@@ -73,7 +73,7 @@ def _sanitize_error_context_value(value: Any) -> Any:
|
|
|
73
73
|
normalized = _sanitize_error_context_value(parsed)
|
|
74
74
|
if isinstance(normalized, dict | list):
|
|
75
75
|
return json.dumps(normalized, separators=(",", ":"))
|
|
76
|
-
except Exception:
|
|
76
|
+
except Exception: # nosec B110 - Safe fallback for malformed JSON in log sanitization
|
|
77
77
|
pass
|
|
78
78
|
return _sanitize_log_excerpt(value)
|
|
79
79
|
|
|
@@ -260,9 +260,7 @@ class AuthCaptureMiddleware:
|
|
|
260
260
|
self.app = app
|
|
261
261
|
self._sse_path = _normalize_path(os.getenv("FASTMCP_SSE_PATH", "/sse"))
|
|
262
262
|
self._message_path = _normalize_path(os.getenv("FASTMCP_MESSAGE_PATH", "/messages"))
|
|
263
|
-
self._streamable_path = _normalize_path(
|
|
264
|
-
os.getenv("FASTMCP_STREAMABLE_HTTP_PATH", "/mcp")
|
|
265
|
-
)
|
|
263
|
+
self._streamable_path = _normalize_path(os.getenv("FASTMCP_STREAMABLE_HTTP_PATH", "/mcp"))
|
|
266
264
|
self._code_mode_path = _normalize_path(os.getenv("ROOTLY_CODE_MODE_PATH", "/mcp-codemode"))
|
|
267
265
|
self._capture_paths = {
|
|
268
266
|
self._sse_path,
|
|
@@ -509,7 +507,9 @@ def strip_heavy_shift_data(data: dict[str, Any]) -> dict[str, Any]:
|
|
|
509
507
|
kept_relationships = {}
|
|
510
508
|
for rel_key in ("user", "shift_override"):
|
|
511
509
|
if rel_key in relationships:
|
|
512
|
-
kept_relationships[rel_key] = _collapse_relationship_data(
|
|
510
|
+
kept_relationships[rel_key] = _collapse_relationship_data(
|
|
511
|
+
relationships[rel_key]
|
|
512
|
+
)
|
|
513
513
|
shift["relationships"] = kept_relationships
|
|
514
514
|
|
|
515
515
|
if isinstance(data["data"], list):
|
|
@@ -857,9 +857,7 @@ class AuthenticatedHTTPXClient:
|
|
|
857
857
|
_record_upstream_response_context(request.method, response)
|
|
858
858
|
|
|
859
859
|
response = self._maybe_strip_alert_response(request.method, str(request.url), response)
|
|
860
|
-
response = self._maybe_strip_collection_response(
|
|
861
|
-
request.method, str(request.url), response
|
|
862
|
-
)
|
|
860
|
+
response = self._maybe_strip_collection_response(request.method, str(request.url), response)
|
|
863
861
|
return response
|
|
864
862
|
|
|
865
863
|
async def __aenter__(self):
|
|
@@ -205,9 +205,9 @@ class TestLocalWithRealToken:
|
|
|
205
205
|
token = os.getenv("ROOTLY_API_TOKEN")
|
|
206
206
|
|
|
207
207
|
# Basic format validation
|
|
208
|
-
assert token is not None and token.startswith(
|
|
209
|
-
"rootly_"
|
|
210
|
-
)
|
|
208
|
+
assert token is not None and token.startswith("rootly_"), (
|
|
209
|
+
"Token should start with 'rootly_'"
|
|
210
|
+
)
|
|
211
211
|
assert token is not None and len(token) > 20, "Token should be reasonably long"
|
|
212
212
|
assert token is not None and "_" in token, "Token should contain underscores"
|
|
213
213
|
|