Skip to content
Open
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
60 changes: 55 additions & 5 deletions integtests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import dataclasses
import importlib.metadata
import json
import logging
import os
Expand All @@ -16,10 +17,11 @@
from kbcstorage.client import Client as SyncStorageClient
from mcp.server.session import ServerSession
from mcp.shared.context import RequestContext
from mcp.types import ClientCapabilities, Implementation, InitializeRequestParams

from keboola_mcp_server.clients.client import KeboolaClient
from keboola_mcp_server.config import Config, ServerRuntimeInfo
from keboola_mcp_server.mcp import ServerState
from keboola_mcp_server.mcp import ServerState, SessionStateMiddleware
from keboola_mcp_server.server import create_server
from keboola_mcp_server.workspace import WorkspaceManager

Expand All @@ -35,6 +37,8 @@
DEV_STORAGE_API_URL_ENV_VAR = 'STORAGE_API_URL'
DEV_STORAGE_TOKEN_ENV_VAR = 'KBC_STORAGE_TOKEN'
DEV_WORKSPACE_SCHEMA_ENV_VAR = 'KBC_WORKSPACE_SCHEMA'
INTEGTEST_CLIENT_INFO = Implementation(name='integtest/mcp', version=importlib.metadata.version('keboola_mcp_server'))
INTEGTEST_USER_AGENT = f'{INTEGTEST_CLIENT_INFO.name}/{INTEGTEST_CLIENT_INFO.version}'


@dataclass(frozen=True)
Expand Down Expand Up @@ -84,6 +88,44 @@ def env_file_loaded() -> bool:
return load_dotenv()


@pytest.fixture(scope='session', autouse=True)
def _patch_fastmcp_client_default_info() -> Generator[None, None, None]:
# Ensure all fastmcp.Client instances in integration tests use a distinct identity
# unless a test intentionally provides a different client_info.
monkeypatch = pytest.MonkeyPatch()
original_init = Client.__init__

def _init_with_integtest_client_info(self, *args: Any, **kwargs: Any) -> None:
kwargs.setdefault('client_info', INTEGTEST_CLIENT_INFO)
original_init(self, *args, **kwargs)

monkeypatch.setattr(Client, '__init__', _init_with_integtest_client_info)
try:
yield
finally:
monkeypatch.undo()


@pytest.fixture(scope='session', autouse=True)
def _patch_session_middleware_user_agent() -> Generator[None, None, None]:
# Force a distinct User-Agent for outbound Keboola API requests during integration tests.
monkeypatch = pytest.MonkeyPatch()
original_get_headers = SessionStateMiddleware._get_headers.__func__

def _get_headers_with_integtest_ua(
cls: type[SessionStateMiddleware], runtime_info: ServerRuntimeInfo
) -> dict[str, Any]:
headers = original_get_headers(cls, runtime_info)
headers['User-Agent'] = INTEGTEST_USER_AGENT
return headers

monkeypatch.setattr(SessionStateMiddleware, '_get_headers', classmethod(_get_headers_with_integtest_ua))
try:
yield
finally:
monkeypatch.undo()


@pytest.fixture(scope='session')
def env_init(env_file_loaded: bool, storage_api_token: str, storage_api_url: str, workspace_schema: str) -> bool:
# We reset the development environment variables to the values of the integtest environment variables.
Expand Down Expand Up @@ -300,7 +342,11 @@ def sync_storage_client(storage_api_token: str, storage_api_url: str) -> SyncSto

@pytest.fixture
def keboola_client(sync_storage_client: SyncStorageClient) -> KeboolaClient:
return KeboolaClient(storage_api_token=sync_storage_client.token, storage_api_url=sync_storage_client.root_url)
return KeboolaClient(
storage_api_token=sync_storage_client.token,
storage_api_url=sync_storage_client.root_url,
headers={'User-Agent': INTEGTEST_USER_AGENT},
)


@pytest.fixture
Expand Down Expand Up @@ -332,8 +378,12 @@ def mcp_context(
KeboolaClient.STATE_KEY: keboola_client,
WorkspaceManager.STATE_KEY: workspace_manager,
}
client_context.session.client_params = None
client_context.client_id = None
client_context.session.client_params = InitializeRequestParams(
protocolVersion='1',
capabilities=ClientCapabilities(),
clientInfo=INTEGTEST_CLIENT_INFO,
)
client_context.client_id = INTEGTEST_USER_AGENT
client_context.session_id = None
client_context.request_context = mocker.MagicMock(RequestContext)
client_context.request_context.lifespan_context = ServerState(mcp_config, ServerRuntimeInfo(transport='stdio'))
Expand All @@ -351,5 +401,5 @@ def mcp_server(storage_api_url: str, storage_api_token: str, workspace_schema: s

@pytest_asyncio.fixture
async def mcp_client(mcp_server: FastMCP) -> AsyncGenerator[Client, None]:
async with Client(mcp_server) as client:
async with Client(mcp_server, client_info=INTEGTEST_CLIENT_INFO) as client:
yield client
7 changes: 4 additions & 3 deletions integtests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
import httpx
import pytest
from fastmcp import Context
from mcp.types import ClientCapabilities, Implementation, InitializeRequestParams
from mcp.types import ClientCapabilities, InitializeRequestParams

from integtests.conftest import INTEGTEST_CLIENT_INFO, INTEGTEST_USER_AGENT
from keboola_mcp_server.clients.client import KeboolaClient
from keboola_mcp_server.errors import tool_errors
from keboola_mcp_server.mcp import CONVERSATION_ID, AggregateError
Expand Down Expand Up @@ -116,7 +117,7 @@ async def test_event_emitted(self, tool_name: str, event_message: str, event_typ
mcp_context.session.client_params = InitializeRequestParams(
protocolVersion='1',
capabilities=ClientCapabilities(),
clientInfo=Implementation(name='integtest', version='1.2.3'),
clientInfo=INTEGTEST_CLIENT_INFO,
)
mcp_context.session.state[CONVERSATION_ID] = '#987654321'
unique = uuid.uuid4().hex
Expand Down Expand Up @@ -145,7 +146,7 @@ async def test_event_emitted(self, tool_name: str, event_message: str, event_typ
assert emitted_event['params']['mcpServerContext'] == {
'appEnv': 'DEV',
'version': distribution('keboola_mcp_server').version,
'userAgent': 'integtest/1.2.3',
'userAgent': INTEGTEST_USER_AGENT,
'sessionId': 'deadbee',
'serverTransport': 'stdio',
'conversationId': '#987654321',
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "keboola-mcp-server"
version = "1.44.8"
version = "1.44.9"
description = "MCP server for interacting with Keboola Connection"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
6 changes: 3 additions & 3 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading