Skip to content

Commit fb4dd6a

Browse files
fix: force shutdown server on exit
1 parent 16309e6 commit fb4dd6a

File tree

10 files changed

+358
-15
lines changed

10 files changed

+358
-15
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ readme = "README.md"
1010
requires-python = ">=3.12"
1111
dependencies = [
1212
"asyncpg>=0.30.0",
13+
"logfire[system-metrics]>=3.12.0",
1314
"mcp[cli]>=1.4.1",
1415
"pglast>=7.3",
1516
"pyyaml>=6.0.2",

smithery.yaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,17 @@ startCommand:
1111
- supabaseProjectRef
1212
- supabaseDbPassword
1313
- supabaseRegion
14+
- queryApiKey
1415
properties:
16+
queryApiKey:
17+
type: string
18+
description: "(required) - Your Query API key"
1519
supabaseProjectRef:
1620
type: string
17-
description: "(required) - Supabase project reference ID - Default: 127.0.0.1:54322"
18-
default: "127.0.0.1:54322"
21+
description: "(required) - Supabase project reference ID"
1922
supabaseDbPassword:
2023
type: string
21-
description: "(required) - Database password - Default: postgres"
22-
default: "postgres"
24+
description: "(required) - Database password"
2325
supabaseRegion:
2426
type: string
2527
description: "(required) - AWS region where your Supabase project is hosted - Default: us-east-1"
@@ -35,6 +37,7 @@ startCommand:
3537
command: 'supabase-mcp-server',
3638
args: [],
3739
env: {
40+
QUERY_API_KEY: config.queryApiKey,
3841
SUPABASE_PROJECT_REF: config.supabaseProjectRef,
3942
SUPABASE_DB_PASSWORD: config.supabaseDbPassword,
4043
SUPABASE_REGION: config.supabaseRegion,

supabase_mcp/clients/api_client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ def __init__(
4141
self.query_api_url = query_api_url or settings.query_api_url
4242
self._check_api_key_set()
4343
self.client: httpx.AsyncClient | None = None
44-
logger.info(f"Query API client initialized with URL: {self.query_api_url}, key: {self.query_api_key}")
44+
logger.info(
45+
f"✔️ Query API client initialized successfully with URL: {self.query_api_url}, with key: {bool(self.query_api_key)}"
46+
)
4547

4648
async def _ensure_client(self) -> httpx.AsyncClient:
4749
"""Ensure client exists and is ready for use.
@@ -53,7 +55,7 @@ async def _ensure_client(self) -> httpx.AsyncClient:
5355
logger.info("Creating new Query API client")
5456
self.client = httpx.AsyncClient(
5557
base_url=self.query_api_url,
56-
headers={"Authorization": f"Bearer {self.query_api_key}"},
58+
headers={"X-API-Key": f"{self.query_api_key}"},
5759
timeout=30.0,
5860
)
5961
logger.info("Returning existing Query API client")
@@ -79,6 +81,7 @@ async def check_feature_access(self, feature_name: str) -> FeatureAccessResponse
7981
method="GET",
8082
path=ApiRoutes.FEATURES_ACCESS.format(feature_name=feature_name),
8183
)
84+
logger.debug(f"Feature access response: {result}")
8285
return FeatureAccessResponse.model_validate(result)
8386
except Exception as e:
8487
logger.error(f"Error checking feature access: {e}")

supabase_mcp/clients/management_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __init__(self, settings: Settings) -> None:
3838
self.settings = settings
3939
self.client = self.create_httpx_client(settings)
4040

41-
logger.info("Initilized Mangement API client successfully")
41+
logger.info("✔️ Management API client initialized successfully")
4242

4343
def create_httpx_client(self, settings: Settings) -> httpx.AsyncClient:
4444
"""Create and configure an httpx client for API requests."""

supabase_mcp/clients/sdk_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def __init__(
4747
self.project_ref = settings.supabase_project_ref if settings else project_ref
4848
self.service_role_key = settings.supabase_service_role_key if settings else service_role_key
4949
self.supabase_url = self.get_supabase_url()
50-
logger.info(f"Initialized Supabase SDK client for project {self.project_ref}")
50+
logger.info(f"✔️ Supabase SDK client initialized successfully for project {self.project_ref}")
5151

5252
def get_supabase_url(self) -> str:
5353
"""Returns the Supabase URL based on the project reference"""

supabase_mcp/core/container.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
from mcp.server.fastmcp import FastMCP
4+
35
from supabase_mcp.clients.api_client import ApiClient
46
from supabase_mcp.clients.management_client import ManagementAPIClient
57
from supabase_mcp.clients.sdk_client import SupabaseSDKClient
@@ -21,7 +23,7 @@ class ServicesContainer:
2123

2224
def __init__(
2325
self,
24-
# mcp_server: FastMCP,
26+
mcp_server: FastMCP | None = None,
2527
postgres_client: PostgresClient | None = None,
2628
api_client: ManagementAPIClient | None = None,
2729
sdk_client: SupabaseSDKClient | None = None,
@@ -34,7 +36,6 @@ def __init__(
3436
feature_manager: FeatureManager | None = None,
3537
) -> None:
3638
"""Create a new container container reference"""
37-
# self.mcp_server = mcp_server
3839
self.postgres_client = postgres_client
3940
self.api_client = api_client
4041
self.api_manager = api_manager
@@ -45,6 +46,7 @@ def __init__(
4546
self.log_manager = log_manager
4647
self.query_api_client = query_api_client
4748
self.feature_manager = feature_manager
49+
self.mcp_server = mcp_server
4850

4951
@classmethod
5052
def get_instance(cls) -> ServicesContainer:

supabase_mcp/main.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections.abc import AsyncGenerator
12
from contextlib import asynccontextmanager
23

34
from mcp.server.fastmcp import FastMCP
@@ -10,8 +11,10 @@
1011

1112
# Create lifespan for the MCP server
1213
@asynccontextmanager
13-
async def lifespan(app: FastMCP):
14+
async def lifespan(app: FastMCP) -> AsyncGenerator[FastMCP, None]:
1415
try:
16+
logger.info("Initializing services")
17+
1518
# Initialize services
1619
services_container = ServicesContainer.get_instance()
1720
services_container.initialize_services(settings)
@@ -20,8 +23,13 @@ async def lifespan(app: FastMCP):
2023
mcp = ToolRegistry(mcp=app, services_container=services_container).register_tools()
2124
yield mcp
2225
finally:
26+
logger.info("Shutting down services")
2327
services_container = ServicesContainer.get_instance()
2428
await services_container.shutdown_services()
29+
# Force kill the entire process - doesn't care about async contexts
30+
import os
31+
32+
os._exit(0) # Use 0 for successful termination
2533

2634

2735
# Create mcp instance
@@ -31,6 +39,7 @@ async def lifespan(app: FastMCP):
3139
def run_server() -> None:
3240
logger.info("Starting Supabase MCP server")
3341
mcp.run()
42+
logger.info("This code runs only if I don't exit in lifespan")
3443

3544

3645
def run_inspector() -> None:

supabase_mcp/services/database/postgres_client.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def __init__(
8282
# Only log once during initialization with clear project info
8383
is_local = self.project_ref.startswith("127.0.0.1")
8484
logger.info(
85-
f"PostgreSQL client initialized for {'local' if is_local else 'remote'} "
85+
f"✔️ PostgreSQL client initialized successfully for {'local' if is_local else 'remote'} "
8686
f"project: {self.project_ref} (region: {self.db_region})"
8787
)
8888

@@ -245,12 +245,13 @@ async def close(self) -> None:
245245
246246
This should be called when shutting down the application.
247247
"""
248+
import asyncio
249+
248250
if self._pool:
249-
await self._pool.close()
251+
await asyncio.wait_for(self._pool.close(), timeout=5.0)
250252
self._pool = None
251-
logger.info("Closed PostgreSQL connection pool")
252253
else:
253-
logger.debug("No connection pool to close")
254+
logger.debug("No PostgreSQL connection pool to close")
254255

255256
@classmethod
256257
async def reset(cls) -> None:

supabase_mcp/settings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ def find_config_file(env_file: str = ".env") -> str | None:
5353
global_config = home / ".config" / "supabase-mcp" / ".env"
5454

5555
if global_config.exists():
56+
logger.error(
57+
f"DEPRECATED: {global_config} is deprecated and will be removed in a future release. "
58+
"Use your IDE's native .json config file to configure access to MCP."
59+
)
5660
return str(global_config)
5761

5862
return None

0 commit comments

Comments
 (0)