Skip to content

Commit 991f5e8

Browse files
authored
Merge pull request #203 from IBM/test-case-updates
Fixes for pytest warnings
2 parents 3a025e9 + 8ba7048 commit 991f5e8

28 files changed

+360
-334
lines changed

mcpgateway/admin.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ async def admin_list_servers(
101101
"""
102102
logger.debug(f"User {user} requested server list")
103103
servers = await server_service.list_servers(db, include_inactive=include_inactive)
104-
return [server.dict(by_alias=True) for server in servers]
104+
return [server.model_dump(by_alias=True) for server in servers]
105105

106106

107107
@admin_router.get("/servers/{server_id}", response_model=ServerRead)
@@ -316,7 +316,7 @@ async def admin_list_resources(
316316
"""
317317
logger.debug(f"User {user} requested resource list")
318318
resources = await resource_service.list_resources(db, include_inactive=include_inactive)
319-
return [resource.dict(by_alias=True) for resource in resources]
319+
return [resource.model_dump(by_alias=True) for resource in resources]
320320

321321

322322
@admin_router.get("/prompts", response_model=List[PromptRead])
@@ -342,7 +342,7 @@ async def admin_list_prompts(
342342
"""
343343
logger.debug(f"User {user} requested prompt list")
344344
prompts = await prompt_service.list_prompts(db, include_inactive=include_inactive)
345-
return [prompt.dict(by_alias=True) for prompt in prompts]
345+
return [prompt.model_dump(by_alias=True) for prompt in prompts]
346346

347347

348348
@admin_router.get("/gateways", response_model=List[GatewayRead])
@@ -368,7 +368,7 @@ async def admin_list_gateways(
368368
"""
369369
logger.debug(f"User {user} requested gateway list")
370370
gateways = await gateway_service.list_gateways(db, include_inactive=include_inactive)
371-
return [gateway.dict(by_alias=True) for gateway in gateways]
371+
return [gateway.model_dump(by_alias=True) for gateway in gateways]
372372

373373

374374
@admin_router.post("/gateways/{gateway_id}/toggle")
@@ -444,6 +444,7 @@ async def admin_ui(
444444
roots = [root.model_dump(by_alias=True) for root in await root_service.list_roots()]
445445
root_path = settings.app_root_path
446446
response = request.app.state.templates.TemplateResponse(
447+
request,
447448
"admin.html",
448449
{
449450
"request": request,
@@ -486,7 +487,7 @@ async def admin_list_tools(
486487
"""
487488
logger.debug(f"User {user} requested tool list")
488489
tools = await tool_service.list_tools(db, include_inactive=include_inactive)
489-
return [tool.dict(by_alias=True) for tool in tools]
490+
return [tool.model_dump(by_alias=True) for tool in tools]
490491

491492

492493
@admin_router.get("/tools/{tool_id}", response_model=ToolRead)
@@ -570,7 +571,7 @@ async def admin_add_tool(
570571
logger.debug(f"Tool data built: {tool_data}")
571572
try:
572573
tool = ToolCreate(**tool_data)
573-
logger.debug(f"Validated tool data: {tool.dict()}")
574+
logger.debug(f"Validated tool data: {tool.model_dump(by_alias=True)}")
574575
await tool_service.register_tool(db, tool)
575576
return JSONResponse(
576577
content={"message": "Tool registered successfully!", "success": True},
@@ -733,7 +734,7 @@ async def admin_get_gateway(gateway_id: str, db: Session = Depends(get_db), user
733734
"""
734735
logger.debug(f"User {user} requested details for gateway ID {gateway_id}")
735736
gateway = await gateway_service.get_gateway(db, gateway_id)
736-
return gateway.dict(by_alias=True)
737+
return gateway.model_dump(by_alias=True)
737738

738739

739740
@admin_router.post("/gateways")
@@ -868,7 +869,7 @@ async def admin_get_resource(uri: str, db: Session = Depends(get_db), user: str
868869
logger.debug(f"User {user} requested details for resource URI {uri}")
869870
resource = await resource_service.get_resource_by_uri(db, uri)
870871
content = await resource_service.read_resource(db, uri)
871-
return {"resource": resource.dict(by_alias=True), "content": content}
872+
return {"resource": resource.model_dump(by_alias=True), "content": content}
872873

873874

874875
@admin_router.post("/resources")
@@ -1023,7 +1024,7 @@ async def admin_get_prompt(name: str, db: Session = Depends(get_db), user: str =
10231024
prompt_details = await prompt_service.get_prompt_details(db, name)
10241025

10251026
prompt = PromptRead.model_validate(prompt_details)
1026-
return prompt.dict(by_alias=True)
1027+
return prompt.model_dump(by_alias=True)
10271028

10281029

10291030
@admin_router.post("/prompts")

mcpgateway/cache/session_registry.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ def __init__(
9494
raise ValueError("Redis backend requires redis_url")
9595

9696
self._redis = Redis.from_url(redis_url)
97-
self._pubsub = self._redis.pubsub()
98-
self._pubsub.subscribe("mcp_session_events")
9997

10098
elif self._backend == "database":
10199
if not SQLALCHEMY_AVAILABLE:
@@ -152,6 +150,10 @@ async def initialize(self) -> None:
152150
self._cleanup_task = asyncio.create_task(self._db_cleanup_task())
153151
logger.info("Database cleanup task started")
154152

153+
elif self._backend == "redis":
154+
self._pubsub = self._redis.pubsub()
155+
await self._pubsub.subscribe("mcp_session_events")
156+
155157
elif self._backend == "none":
156158
# Nothing to initialize for none backend
157159
pass
@@ -179,8 +181,8 @@ async def shutdown(self) -> None:
179181
# Close Redis connections
180182
if self._backend == "redis":
181183
try:
182-
self._pubsub.close()
183-
self._redis.close()
184+
await self._pubsub.aclose()
185+
await self._redis.aclose()
184186
except Exception as e:
185187
logger.error(f"Error closing Redis connection: {e}")
186188

mcpgateway/config.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,22 @@
4141
import jq
4242
from jsonpath_ng.ext import parse
4343
from jsonpath_ng.jsonpath import JSONPath
44-
from pydantic import Field, field_validator
44+
from pydantic import field_validator
4545
from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict
4646

4747

4848
class Settings(BaseSettings):
4949
"""MCP Gateway configuration settings."""
5050

5151
# Basic Settings
52-
app_name: str = Field("MCP_Gateway", env="APP_NAME")
53-
host: str = Field("127.0.0.1", env="HOST")
54-
port: int = Field(4444, env="PORT")
52+
app_name: str = "MCP_Gateway"
53+
host: str = "127.0.0.1"
54+
port: int = 4444
5555
database_url: str = "sqlite:///./mcp.db"
5656
templates_dir: Path = Path("mcpgateway/templates")
5757
# Absolute paths resolved at import-time (still override-able via env vars)
58-
templates_dir: Path = Field(
59-
default=files("mcpgateway") / "templates",
60-
env="TEMPLATES_DIR",
61-
)
62-
static_dir: Path = Field(
63-
default=files("mcpgateway") / "static",
64-
env="STATIC_DIR",
65-
)
58+
templates_dir: Path = files("mcpgateway") / "templates"
59+
static_dir: Path = files("mcpgateway") / "static"
6660
app_root_path: str = ""
6761

6862
# Protocol

mcpgateway/db.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@
105105
connect_args=connect_args,
106106
)
107107

108+
109+
# ---------------------------------------------------------------------------
110+
# 6. Function to return UTC timestamp
111+
# ---------------------------------------------------------------------------
112+
def utc_now():
113+
return datetime.now(timezone.utc)
114+
115+
108116
# Session factory
109117
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
110118

@@ -181,7 +189,7 @@ class ToolMetric(Base):
181189

182190
id: Mapped[int] = mapped_column(primary_key=True)
183191
tool_id: Mapped[str] = mapped_column(String, ForeignKey("tools.id"), nullable=False)
184-
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
192+
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
185193
response_time: Mapped[float] = mapped_column(Float, nullable=False)
186194
is_success: Mapped[bool] = mapped_column(Boolean, nullable=False)
187195
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
@@ -207,7 +215,7 @@ class ResourceMetric(Base):
207215

208216
id: Mapped[int] = mapped_column(primary_key=True)
209217
resource_id: Mapped[int] = mapped_column(Integer, ForeignKey("resources.id"), nullable=False)
210-
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
218+
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
211219
response_time: Mapped[float] = mapped_column(Float, nullable=False)
212220
is_success: Mapped[bool] = mapped_column(Boolean, nullable=False)
213221
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
@@ -233,7 +241,7 @@ class ServerMetric(Base):
233241

234242
id: Mapped[int] = mapped_column(primary_key=True)
235243
server_id: Mapped[str] = mapped_column(String, ForeignKey("servers.id"), nullable=False)
236-
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
244+
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
237245
response_time: Mapped[float] = mapped_column(Float, nullable=False)
238246
is_success: Mapped[bool] = mapped_column(Boolean, nullable=False)
239247
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
@@ -259,7 +267,7 @@ class PromptMetric(Base):
259267

260268
id: Mapped[int] = mapped_column(primary_key=True)
261269
prompt_id: Mapped[int] = mapped_column(Integer, ForeignKey("prompts.id"), nullable=False)
262-
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
270+
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
263271
response_time: Mapped[float] = mapped_column(Float, nullable=False)
264272
is_success: Mapped[bool] = mapped_column(Boolean, nullable=False)
265273
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
@@ -312,8 +320,8 @@ class Tool(Base):
312320
headers: Mapped[Optional[Dict[str, str]]] = mapped_column(JSON)
313321
input_schema: Mapped[Dict[str, Any]] = mapped_column(JSON)
314322
annotations: Mapped[Optional[Dict[str, Any]]] = mapped_column(JSON, default=lambda: {})
315-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
316-
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
323+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
324+
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now, onupdate=utc_now)
317325
is_active: Mapped[bool] = mapped_column(default=True)
318326
jsonpath_filter: Mapped[str] = mapped_column(default="")
319327

@@ -557,8 +565,8 @@ class Resource(Base):
557565
mime_type: Mapped[Optional[str]]
558566
size: Mapped[Optional[int]]
559567
template: Mapped[Optional[str]] # URI template for parameterized resources
560-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
561-
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
568+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
569+
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now, onupdate=utc_now)
562570
is_active: Mapped[bool] = mapped_column(default=True)
563571
metrics: Mapped[List["ResourceMetric"]] = relationship("ResourceMetric", back_populates="resource", cascade="all, delete-orphan")
564572

@@ -714,7 +722,7 @@ class ResourceSubscription(Base):
714722
id: Mapped[int] = mapped_column(primary_key=True)
715723
resource_id: Mapped[int] = mapped_column(ForeignKey("resources.id"))
716724
subscriber_id: Mapped[str] # Client identifier
717-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
725+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
718726
last_notification: Mapped[Optional[datetime]] = mapped_column(DateTime)
719727

720728
resource: Mapped["Resource"] = relationship(back_populates="subscriptions")
@@ -745,8 +753,8 @@ class Prompt(Base):
745753
description: Mapped[Optional[str]]
746754
template: Mapped[str] = mapped_column(Text)
747755
argument_schema: Mapped[Dict[str, Any]] = mapped_column(JSON)
748-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
749-
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
756+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
757+
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now, onupdate=utc_now)
750758
is_active: Mapped[bool] = mapped_column(default=True)
751759
metrics: Mapped[List["PromptMetric"]] = relationship("PromptMetric", back_populates="prompt", cascade="all, delete-orphan")
752760

@@ -894,8 +902,8 @@ class Server(Base):
894902
name: Mapped[str] = mapped_column(unique=True)
895903
description: Mapped[Optional[str]]
896904
icon: Mapped[Optional[str]]
897-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
898-
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
905+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
906+
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now, onupdate=utc_now)
899907
is_active: Mapped[bool] = mapped_column(default=True)
900908
metrics: Mapped[List["ServerMetric"]] = relationship("ServerMetric", back_populates="server", cascade="all, delete-orphan")
901909

@@ -1014,8 +1022,8 @@ class Gateway(Base):
10141022
description: Mapped[Optional[str]]
10151023
transport: Mapped[str] = mapped_column(default="SSE")
10161024
capabilities: Mapped[Dict[str, Any]] = mapped_column(JSON)
1017-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
1018-
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
1025+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
1026+
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now, onupdate=utc_now)
10191027
is_active: Mapped[bool] = mapped_column(default=True)
10201028
last_seen: Mapped[Optional[datetime]]
10211029

@@ -1086,8 +1094,8 @@ class SessionRecord(Base):
10861094
__tablename__ = "mcp_sessions"
10871095

10881096
session_id: Mapped[str] = mapped_column(primary_key=True)
1089-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) # pylint: disable=not-callable
1090-
last_accessed: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) # pylint: disable=not-callable
1097+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now) # pylint: disable=not-callable
1098+
last_accessed: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now, onupdate=utc_now) # pylint: disable=not-callable
10911099
data: Mapped[str] = mapped_column(String, nullable=True)
10921100

10931101
messages: Mapped[List["SessionMessageRecord"]] = relationship("SessionMessageRecord", back_populates="session", cascade="all, delete-orphan")
@@ -1101,8 +1109,8 @@ class SessionMessageRecord(Base):
11011109
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
11021110
session_id: Mapped[str] = mapped_column(ForeignKey("mcp_sessions.session_id"))
11031111
message: Mapped[str] = mapped_column(String, nullable=True)
1104-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) # pylint: disable=not-callable
1105-
last_accessed: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) # pylint: disable=not-callable
1112+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now) # pylint: disable=not-callable
1113+
last_accessed: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now, onupdate=utc_now) # pylint: disable=not-callable
11061114

11071115
session: Mapped["SessionRecord"] = relationship("SessionRecord", back_populates="messages")
11081116

mcpgateway/federation/discovery.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# Standard
1717
import asyncio
1818
from dataclasses import dataclass
19-
from datetime import datetime, timedelta
19+
from datetime import datetime, timedelta, timezone
2020
import logging
2121
import os
2222
import socket
@@ -205,7 +205,7 @@ async def add_peer(self, url: str, source: str, name: Optional[str] = None) -> b
205205
# Skip if already known
206206
if url in self._discovered_peers:
207207
peer = self._discovered_peers[url]
208-
peer.last_seen = datetime.utcnow()
208+
peer.last_seen = datetime.now(timezone.utc)
209209
return False
210210

211211
try:
@@ -218,8 +218,8 @@ async def add_peer(self, url: str, source: str, name: Optional[str] = None) -> b
218218
name=name,
219219
protocol_version=PROTOCOL_VERSION,
220220
capabilities=capabilities,
221-
discovered_at=datetime.utcnow(),
222-
last_seen=datetime.utcnow(),
221+
discovered_at=datetime.now(timezone.utc),
222+
last_seen=datetime.now(timezone.utc),
223223
source=source,
224224
)
225225

@@ -253,7 +253,7 @@ async def refresh_peer(self, url: str) -> bool:
253253
try:
254254
capabilities = await self._get_gateway_info(url)
255255
self._discovered_peers[url].capabilities = capabilities
256-
self._discovered_peers[url].last_seen = datetime.utcnow()
256+
self._discovered_peers[url].last_seen = datetime.now(timezone.utc)
257257
return True
258258
except Exception as e:
259259
logger.warning(f"Failed to refresh peer {url}: {e}")
@@ -303,7 +303,7 @@ async def _cleanup_loop(self) -> None:
303303
"""Periodically clean up stale peers."""
304304
while True:
305305
try:
306-
now = datetime.utcnow()
306+
now = datetime.now(timezone.utc)
307307
stale_urls = [url for url, peer in self._discovered_peers.items() if now - peer.last_seen > timedelta(minutes=10)]
308308
for url in stale_urls:
309309
await self.remove_peer(url)
@@ -363,7 +363,7 @@ async def _get_gateway_info(self, url: str) -> ServerCapabilities:
363363
if result.get("protocol_version") != PROTOCOL_VERSION:
364364
raise ValueError(f"Unsupported protocol version: {result.get('protocol_version')}")
365365

366-
return ServerCapabilities.parse_obj(result["capabilities"])
366+
return ServerCapabilities.model_validate(result["capabilities"])
367367

368368
async def _exchange_peers(self) -> None:
369369
"""Exchange peer lists with known gateways."""

mcpgateway/federation/forward.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
# Standard
1717
import asyncio
18-
from datetime import datetime
18+
from datetime import datetime, timezone
1919
import logging
2020
from typing import Any, Dict, List, Optional, Set, Tuple, Union
2121

@@ -233,7 +233,7 @@ async def _forward_to_gateway(
233233
result = response.json()
234234

235235
# Update last seen
236-
gateway.last_seen = datetime.utcnow()
236+
gateway.last_seen = datetime.now(timezone.utc)
237237

238238
# Handle response
239239
if "error" in result:
@@ -316,7 +316,7 @@ def _check_rate_limit(self, gateway_url: str) -> bool:
316316
Returns:
317317
True if request allowed
318318
"""
319-
now = datetime.utcnow()
319+
now = datetime.now(timezone.utc)
320320

321321
# Clean old history
322322
self._request_history[gateway_url] = [t for t in self._request_history.get(gateway_url, []) if (now - t).total_seconds() < 60]

0 commit comments

Comments
 (0)