Skip to content

Commit f205487

Browse files
committed
Fail fast if Grafana override settings are not provided in sse request
1 parent 87d4168 commit f205487

File tree

2 files changed

+37
-3
lines changed

2 files changed

+37
-3
lines changed

src/mcp_grafana/middleware.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from mcp.server import FastMCP
44
from starlette.datastructures import Headers
5+
from starlette.exceptions import HTTPException
56

67
from .client import GrafanaClient, grafana_client
78
from .settings import GrafanaSettings, grafana_settings
@@ -36,8 +37,9 @@ class GrafanaMiddleware:
3637
This should be used as a context manager before handling the /sse request.
3738
"""
3839

39-
def __init__(self, request):
40+
def __init__(self, request, fail_if_unset=True):
4041
self.request = request
42+
self.fail_if_unset = fail_if_unset
4143
self.settings_token = None
4244
self.client_token = None
4345

@@ -53,6 +55,8 @@ async def __aenter__(self):
5355
self.client_token = grafana_client.set(
5456
GrafanaClient.from_settings(new_settings)
5557
)
58+
elif self.fail_if_unset:
59+
raise HTTPException(status_code=403, detail="No Grafana settings found.")
5660

5761
async def __aexit__(self, exc_type, exc_val, exc_tb):
5862
if self.settings_token is not None:

tests/middleware_test.py

+32-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import anyio
77
import httpx
8+
from mcp.server import FastMCP
89
from mcp.types import (
910
LATEST_PROTOCOL_VERSION,
1011
CallToolResult,
@@ -20,13 +21,21 @@
2021
)
2122
import pytest
2223
from httpx_sse import aconnect_sse
24+
from starlette.exceptions import HTTPException
2325

24-
from mcp_grafana import mcp
26+
from mcp_grafana.tools import add_tools
2527
from mcp_grafana.middleware import run_sse_async_with_middleware
2628

2729
from pytest_httpserver import HTTPServer
2830

2931

32+
@pytest.fixture
33+
def mcp():
34+
mcp = FastMCP("grafana")
35+
add_tools(mcp)
36+
return mcp
37+
38+
3039
class TestMiddleware:
3140
"""
3241
Test that our injected starlette middleware extracts headers and
@@ -36,7 +45,28 @@ class TestMiddleware:
3645
"""
3746

3847
@pytest.mark.asyncio
39-
async def test_multiple_requests(self):
48+
async def test_no_headers_provided(self, mcp: FastMCP):
49+
"""
50+
Ensure that the middleware fails if no headers are provided.
51+
"""
52+
53+
# Monkeypatch the MCP server to use our middleware.
54+
mcp.run_sse_async = MethodType(run_sse_async_with_middleware, mcp)
55+
mcp.settings.host = "127.0.0.1"
56+
mcp.settings.port = 9500
57+
async with anyio.create_task_group() as tg:
58+
tg.start_soon(mcp.run_sse_async, name="mcp")
59+
# Wait for the server to start.
60+
await asyncio.sleep(0.1)
61+
client = httpx.AsyncClient(
62+
base_url=f"http://{mcp.settings.host}:{mcp.settings.port}"
63+
)
64+
resp = await client.get("/sse")
65+
assert resp.status_code == httpx.codes.FORBIDDEN
66+
tg.cancel_scope.cancel()
67+
68+
@pytest.mark.asyncio
69+
async def test_multiple_requests(self, mcp: FastMCP):
4070
"""
4171
Ensure that the contextvars do not leak across requests.
4272

0 commit comments

Comments
 (0)