Skip to content
Merged
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
2 changes: 1 addition & 1 deletion docker/pyproject.deps.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "mcp-plex"
version = "2.0.12"
version = "2.0.13"
requires-python = ">=3.11,<3.13"
dependencies = [
"fastmcp>=2.11.2",
Expand Down
49 changes: 39 additions & 10 deletions mcp_plex/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,21 @@ def __init__(
host = self.settings.qdrant_host
if location is None and host is None:
location = ":memory:"
self.qdrant_client = qdrant_client or AsyncQdrantClient(
location=location,
api_key=self.settings.qdrant_api_key,
host=host,
port=self.settings.qdrant_port,
grpc_port=self.settings.qdrant_grpc_port,
prefer_grpc=self.settings.qdrant_prefer_grpc,
https=self.settings.qdrant_https,
)
self._qdrant_config = {
"location": location,
"api_key": self.settings.qdrant_api_key,
"host": host,
"port": self.settings.qdrant_port,
"grpc_port": self.settings.qdrant_grpc_port,
"prefer_grpc": self.settings.qdrant_prefer_grpc,
"https": self.settings.qdrant_https,
}
self._qdrant_client_factory = None
if qdrant_client is None:
self._qdrant_client_factory = self._build_default_qdrant_client
self._qdrant_client = self._build_default_qdrant_client()
else:
self._qdrant_client = qdrant_client

class _ServerLifespan:
def __init__(self, plex_server: "PlexServer") -> None:
Expand All @@ -103,8 +109,31 @@ def _lifespan(app: FastMCP) -> _ServerLifespan: # noqa: ARG001
self._plex_client: PlexServerClient | None = None
self._plex_client_lock = asyncio.Lock()

def _build_default_qdrant_client(self) -> AsyncQdrantClient:
"""Construct a new Qdrant client using the server settings."""

return AsyncQdrantClient(**self._qdrant_config)

@property
def qdrant_client(self) -> AsyncQdrantClient:
if self._qdrant_client is None:
if self._qdrant_client_factory is None:
raise RuntimeError("Qdrant client is not configured")
self._qdrant_client = self._qdrant_client_factory()
return self._qdrant_client

@qdrant_client.setter
def qdrant_client(self, client: AsyncQdrantClient | None) -> None:
self._qdrant_client = client
if client is None:
return
self._qdrant_client_factory = None

async def close(self) -> None:
await self.qdrant_client.close()
if self._qdrant_client is not None:
await self._qdrant_client.close()
if self._qdrant_client_factory is not None:
self._qdrant_client = None
self._plex_client = None
self._plex_identity = None

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 = "setuptools.build_meta"

[project]
name = "mcp-plex"
version = "2.0.12"
version = "2.0.13"

description = "Plex-Oriented Model Context Protocol Server"
requires-python = ">=3.11,<3.13"
Expand Down
25 changes: 25 additions & 0 deletions tests/test_server_internal_additional.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,31 @@ async def close(self) -> None:
assert plex_server._plex_client is None


def test_qdrant_client_reinitializes_after_close(monkeypatch):
from qdrant_client import async_qdrant_client

instances: list[object] = []

class StubClient:
def __init__(self, *args, **kwargs):
instances.append(self)

async def close(self) -> None:
return None

monkeypatch.setattr(async_qdrant_client, "AsyncQdrantClient", StubClient)
reloaded = importlib.reload(server_module)
try:
first = reloaded.server.qdrant_client
asyncio.run(reloaded.server.close())
second = reloaded.server.qdrant_client
assert first is not second
assert len(instances) >= 2
finally:
asyncio.run(reloaded.server.close())
importlib.reload(reloaded)


def test_request_model_skips_variadic_params():
def _callable(arg, *args, **kwargs):
return arg
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

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