Skip to content

Commit a88ee4a

Browse files
Merge pull request #62 from alexander-zuev/feat/api-server
feat: introduce api client <> server separation
2 parents d674529 + 0a89712 commit a88ee4a

35 files changed

+1330
-257
lines changed

.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Supabase MCP Server Environment Configuration
22
# Copy this file to .env to configure your server
33

4+
# API Key
5+
QUERY_API_KEY=your-api-key # thequery.dev API key
6+
47
# Required for remote Supabase projects (optional for local development)
58
SUPABASE_PROJECT_REF=your-project-ref # Your project reference from dashboard URL
69
SUPABASE_DB_PASSWORD=your-db-password # Database password for your project
@@ -10,3 +13,7 @@ SUPABASE_REGION=us-east-1 # Region where your Supabase project is hosted
1013
# Optional configuration
1114
SUPABASE_ACCESS_TOKEN=your-personal-access-token # Required for Management API tools
1215
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key # Required for Auth Admin SDK tools
16+
17+
18+
# ONLY for local development
19+
QUERY_API_URL=http://127.0.0.1:8080/v1 # TheQuery.dev API URL when developing locally

.env.test.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ SUPABASE_DB_PASSWORD=postgres
1010

1111
# Optional: Service role key (for auth tests)
1212
# SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
13+
14+
# TheQuery.dev API URL
15+
QUERY_API_URL=http://127.0.0.1:8080/v1

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,16 @@
3535
</p>
3636

3737

38-
## 🎉 The Future of Supabase MCP Server -> Query MCP
38+
## 🎉 Query MCP 🎉
3939

40-
**I'm thrilled to announce that Supabase MCP Server is evolving into [thequery.dev](https://thequery.dev)!**
40+
**I'm thrilled to announce the future of this MCP server - [thequery.dev](https://thequery.dev)!**
4141

4242
While I have big plans for the future, I want to make these commitments super clear:
43-
- **The core tool will stay free forever** - free & open-source software is how I got into coding
43+
- **The core tool will stay free forever** - free & open-source software is how I got into coding and intend to keep this MCP server this way
4444
- **Premium features will be added on top** - enhancing capabilities without limiting existing functionality
45-
- **First 2,000 early adopters will get special perks** - join early for an exclusive treat!
45+
- **All 300+ early adopters will get exclusive perks when paid plans land** - stay tuned!
4646

47-
**🚀 BIG v4 Launch Coming Soon!**
47+
**🚀 Early Access is Live!**
4848

4949
[**👉 Join Early Access at thequery.dev**](https://thequery.dev)
5050

@@ -574,6 +574,7 @@ You will have to confirm and approve every high-risk operation explicitly in ord
574574
- 📝 Automatic versioning of database changes ✅ (v0.3.8)
575575
- 📖 Radically improved knowledge and tools of api spec ✅ (v0.3.8)
576576
- ✍️ Improved consistency of migration-related tools for a more organized database vcs ✅ (v0.3.10)
577+
- 🥳 Query MCP is released (v0.4.0)
577578

578579

579580
For a more detailed roadmap, please see this [discussion](https://github.com/alexander-zuev/supabase-mcp-server/discussions/46) on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ readme = "README.md"
1010
requires-python = ">=3.12"
1111
dependencies = [
1212
"asyncpg>=0.30.0",
13-
"mcp[cli]>=1.2.1",
13+
"logfire[system-metrics]>=3.12.0",
14+
"mcp[cli]>=1.4.1",
1415
"pglast>=7.3",
1516
"pyyaml>=6.0.2",
1617
"supabase>=2.13.0",

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: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import httpx
2+
from pydantic import BaseModel
3+
4+
from supabase_mcp.clients.base_http_client import AsyncHTTPClient
5+
from supabase_mcp.logger import logger
6+
from supabase_mcp.settings import settings
7+
8+
9+
class ApiRoutes:
10+
"""Routes for the Query API"""
11+
12+
FEATURES_ACCESS = "/features/{feature_name}/access"
13+
14+
15+
class FeatureAccessRequest(BaseModel):
16+
"""Request for feature access. Later can be extended with additional metadata."""
17+
18+
feature_name: str
19+
20+
21+
class FeatureAccessResponse(BaseModel):
22+
"""Response for feature access. Later can be extended with additional metadata."""
23+
24+
access_granted: bool
25+
26+
27+
class ApiClient(AsyncHTTPClient):
28+
"""Client for communicating with the Query API server for premium features.
29+
30+
To preserve backwards compatibility and ensure a smooth UX for existing users,
31+
API key is not required as of now.
32+
"""
33+
34+
def __init__(
35+
self,
36+
query_api_key: str | None = None,
37+
query_api_url: str | None = None,
38+
):
39+
"""Initialize the Query API client"""
40+
self.query_api_key = query_api_key or settings.query_api_key
41+
self.query_api_url = query_api_url or settings.query_api_url
42+
self._check_api_key_set()
43+
self.client: httpx.AsyncClient | None = None
44+
logger.info(
45+
f"✔️ Query API client initialized successfully with URL: {self.query_api_url}, with key: {bool(self.query_api_key)}"
46+
)
47+
48+
async def _ensure_client(self) -> httpx.AsyncClient:
49+
"""Ensure client exists and is ready for use.
50+
51+
Creates the client if it doesn't exist yet.
52+
Returns the client instance.
53+
"""
54+
if self.client is None:
55+
logger.info("Creating new Query API client")
56+
self.client = httpx.AsyncClient(
57+
base_url=self.query_api_url,
58+
headers={"X-API-Key": f"{self.query_api_key}"},
59+
timeout=30.0,
60+
)
61+
logger.info("Returning existing Query API client")
62+
return self.client
63+
64+
async def close(self) -> None:
65+
"""Close the client and release resources."""
66+
if self.client:
67+
await self.client.aclose()
68+
logger.info("Query API client closed")
69+
70+
def _check_api_key_set(self) -> None:
71+
"""Check if the API key is set"""
72+
if not self.query_api_key:
73+
logger.warning("Query API key is not set. Only free features will be available.")
74+
return
75+
76+
async def check_feature_access(self, feature_name: str) -> FeatureAccessResponse:
77+
"""Check if the feature is available for the user"""
78+
79+
try:
80+
result = await self.execute_request(
81+
method="GET",
82+
path=ApiRoutes.FEATURES_ACCESS.format(feature_name=feature_name),
83+
)
84+
logger.debug(f"Feature access response: {result}")
85+
return FeatureAccessResponse.model_validate(result)
86+
except Exception as e:
87+
logger.error(f"Error checking feature access: {e}")
88+
raise e

0 commit comments

Comments
 (0)