rootly-mcp-server 2.2.0__tar.gz → 2.2.2__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.0 → rootly_mcp_server-2.2.2}/.gitignore +1 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/PKG-INFO +1 -1
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/pyproject.toml +1 -1
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/server.py +14 -9
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_http_headers.py +41 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.beads/issues.jsonl +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.gitattributes +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.github/dependabot.yml +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.github/workflows/ci.yml +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.github/workflows/lint.yml +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.github/workflows/pypi-release.yml +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.github/workflows/test.yml +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.semaphore/deploy.yml +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.semaphore/semaphore.yml +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/.semaphore/update-task-definition.sh +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/CONTRIBUTING.md +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/Dockerfile +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/LICENSE +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/README.md +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/examples/skills/README.md +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/rootly-mcp-server-demo.gif +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/rootly_openapi.json +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/scripts/setup-hooks.sh +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/__init__.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/__main__.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/client.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/data/__init__.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/exceptions.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/monitoring.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/och_client.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/pagination.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/security.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/smart_utils.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/texttest.json +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/utils.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/src/rootly_mcp_server/validators.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/README.md +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/conftest.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/integration/local/test_basic.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/integration/local/test_smart_tools.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/integration/remote/test_essential.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/test_client.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_authentication.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_exceptions.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_och_client.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_oncall_handoff.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_oncall_metrics.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_oncall_new_tools.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_security.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_server.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_smart_utils.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_tools.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_utils.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/unit/test_validators.py +0 -0
- {rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rootly-mcp-server
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "rootly-mcp-server"
|
|
3
|
-
version = "2.2.
|
|
3
|
+
version = "2.2.2"
|
|
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.10"
|
|
@@ -386,15 +386,20 @@ class AuthenticatedHTTPXClient:
|
|
|
386
386
|
if "params" in kwargs:
|
|
387
387
|
kwargs["params"] = self._transform_params(kwargs["params"])
|
|
388
388
|
|
|
389
|
-
#
|
|
390
|
-
# This is critical because
|
|
391
|
-
#
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
389
|
+
# ALWAYS ensure Content-Type and Accept headers are set correctly for Rootly API
|
|
390
|
+
# This is critical because:
|
|
391
|
+
# 1. FastMCP's get_http_headers() returns LOWERCASE header keys (e.g., "content-type")
|
|
392
|
+
# 2. We must remove any existing content-type/accept and set the correct JSON-API values
|
|
393
|
+
# 3. Handle both lowercase and mixed-case variants to be safe
|
|
394
|
+
headers = dict(kwargs.get("headers") or {})
|
|
395
|
+
# Remove any existing content-type and accept headers (case-insensitive)
|
|
396
|
+
headers_to_remove = [k for k in headers if k.lower() in ("content-type", "accept")]
|
|
397
|
+
for key in headers_to_remove:
|
|
398
|
+
del headers[key]
|
|
399
|
+
# Set the correct JSON-API headers
|
|
400
|
+
headers["Content-Type"] = "application/vnd.api+json"
|
|
401
|
+
headers["Accept"] = "application/vnd.api+json"
|
|
402
|
+
kwargs["headers"] = headers
|
|
398
403
|
|
|
399
404
|
# Call the underlying client's request method and let it handle everything
|
|
400
405
|
return await self.client.request(method, url, **kwargs)
|
|
@@ -275,3 +275,44 @@ class TestFastMCPIntegrationScenario:
|
|
|
275
275
|
call_kwargs = mock_httpx_client.request.call_args[1]
|
|
276
276
|
assert call_kwargs["headers"]["Content-Type"] == "application/vnd.api+json"
|
|
277
277
|
assert call_kwargs["headers"]["Accept"] == "application/vnd.api+json"
|
|
278
|
+
|
|
279
|
+
@pytest.mark.asyncio
|
|
280
|
+
async def test_lowercase_headers_removed_not_duplicated(self, mock_httpx_client):
|
|
281
|
+
"""Test that lowercase headers from FastMCP are removed, not duplicated.
|
|
282
|
+
|
|
283
|
+
FastMCP's get_http_headers() returns lowercase keys like 'content-type'.
|
|
284
|
+
If we only SET 'Content-Type' without removing 'content-type', the dict
|
|
285
|
+
would have BOTH keys, causing the 415 error on the hosted server.
|
|
286
|
+
|
|
287
|
+
This is the ROOT CAUSE of the 415 error that only happens on hosted MCP:
|
|
288
|
+
- Local (stdio): No HTTP request context, get_http_headers() returns {}
|
|
289
|
+
- Hosted (SSE): HTTP request context exists, get_http_headers() returns lowercase headers
|
|
290
|
+
"""
|
|
291
|
+
from rootly_mcp_server.server import AuthenticatedHTTPXClient
|
|
292
|
+
|
|
293
|
+
with patch.object(AuthenticatedHTTPXClient, "_get_api_token", return_value="test-token"):
|
|
294
|
+
client = AuthenticatedHTTPXClient()
|
|
295
|
+
client.client = mock_httpx_client
|
|
296
|
+
|
|
297
|
+
# FastMCP returns lowercase header keys from get_http_headers()
|
|
298
|
+
fastmcp_headers = {
|
|
299
|
+
"content-type": "application/json", # lowercase from FastMCP!
|
|
300
|
+
"accept": "application/json", # lowercase from FastMCP!
|
|
301
|
+
"authorization": "Bearer token",
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await client.request("GET", "/v1/teams", headers=fastmcp_headers)
|
|
305
|
+
|
|
306
|
+
call_kwargs = mock_httpx_client.request.call_args[1]
|
|
307
|
+
headers = call_kwargs["headers"]
|
|
308
|
+
|
|
309
|
+
# Should have correct Content-Type (mixed case)
|
|
310
|
+
assert headers["Content-Type"] == "application/vnd.api+json"
|
|
311
|
+
assert headers["Accept"] == "application/vnd.api+json"
|
|
312
|
+
|
|
313
|
+
# Should NOT have lowercase duplicates - this is the key assertion!
|
|
314
|
+
assert "content-type" not in headers, "lowercase content-type should be removed"
|
|
315
|
+
assert "accept" not in headers, "lowercase accept should be removed"
|
|
316
|
+
|
|
317
|
+
# Other headers should be preserved
|
|
318
|
+
assert headers["authorization"] == "Bearer token"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/integration/local/test_smart_tools.py
RENAMED
|
File without changes
|
{rootly_mcp_server-2.2.0 → rootly_mcp_server-2.2.2}/tests/integration/remote/test_essential.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|