iflow-mcp_yugabyte-yugabytedb-mcp-server 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info → iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info}/METADATA +1 -1
- iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info/RECORD +7 -0
- server.py +26 -23
- iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info/RECORD +0 -7
- {iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info → iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info}/WHEEL +0 -0
- {iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info → iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info}/entry_points.txt +0 -0
- {iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info → iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info}/licenses/LICENSE +0 -0
- {iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info → iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
server.py,sha256=ZDllYa-YCwuY6GUFIksIu6vx6Up1vnTQy-mn1Ig8qDo,9512
|
|
2
|
+
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
3
|
+
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info/METADATA,sha256=03AFzfOEsp7Wa7uIVY3caJ1wN-WED5crkDd5KSRKWJw,9655
|
|
4
|
+
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
5
|
+
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info/entry_points.txt,sha256=0qR8G3C-z5Jb99iC45ysBZsnVGYgPefWGinP2Bhq9hE,54
|
|
6
|
+
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info/top_level.txt,sha256=StKOSmRhvWS5IPcvhsDRbtxUTEofJgYFGOu5AAJdSWo,7
|
|
7
|
+
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.5.dist-info/RECORD,,
|
server.py
CHANGED
|
@@ -13,6 +13,9 @@ from fastapi import FastAPI
|
|
|
13
13
|
from starlette.responses import JSONResponse
|
|
14
14
|
import boto3
|
|
15
15
|
|
|
16
|
+
# Global config variable
|
|
17
|
+
CONFIG: ServerConfig | None = None
|
|
18
|
+
|
|
16
19
|
@dataclass
|
|
17
20
|
class AppContext:
|
|
18
21
|
conn: psycopg2.extensions.connection | None = None
|
|
@@ -42,13 +45,13 @@ def normalize_pem(pem: str) -> str:
|
|
|
42
45
|
return pem + "\n"
|
|
43
46
|
|
|
44
47
|
|
|
45
|
-
def write_root_cert(
|
|
46
|
-
if not
|
|
48
|
+
def write_root_cert():
|
|
49
|
+
if not CONFIG or not CONFIG.ssl_root_cert_secret_arn:
|
|
47
50
|
return None
|
|
48
51
|
|
|
49
52
|
try:
|
|
50
|
-
sm = boto3.client("secretsmanager", region_name=
|
|
51
|
-
resp = sm.get_secret_value(SecretId=
|
|
53
|
+
sm = boto3.client("secretsmanager", region_name=CONFIG.ssl_root_cert_secret_region)
|
|
54
|
+
resp = sm.get_secret_value(SecretId=CONFIG.ssl_root_cert_secret_arn)
|
|
52
55
|
secret_string = resp["SecretString"]
|
|
53
56
|
|
|
54
57
|
# If raw PEM, just use it
|
|
@@ -57,10 +60,10 @@ def write_root_cert(config: ServerConfig):
|
|
|
57
60
|
else:
|
|
58
61
|
data = json.loads(secret_string)
|
|
59
62
|
|
|
60
|
-
if
|
|
61
|
-
if
|
|
62
|
-
raise RuntimeError(f"Certificate key '{
|
|
63
|
-
pem = data[
|
|
63
|
+
if CONFIG.ssl_root_cert_key:
|
|
64
|
+
if CONFIG.ssl_root_cert_key not in data:
|
|
65
|
+
raise RuntimeError(f"Certificate key '{CONFIG.ssl_root_cert_key}' not found in secret")
|
|
66
|
+
pem = data[CONFIG.ssl_root_cert_key]
|
|
64
67
|
else:
|
|
65
68
|
# Backward-compatible: allow exactly one entry
|
|
66
69
|
if len(data) != 1:
|
|
@@ -70,10 +73,10 @@ def write_root_cert(config: ServerConfig):
|
|
|
70
73
|
pem = next(iter(data.values()))
|
|
71
74
|
|
|
72
75
|
pem = normalize_pem(pem)
|
|
73
|
-
with open(
|
|
76
|
+
with open(CONFIG.ssl_root_cert_path, "w") as f:
|
|
74
77
|
f.write(pem.strip() + "\n")
|
|
75
78
|
|
|
76
|
-
return
|
|
79
|
+
return CONFIG.ssl_root_cert_path
|
|
77
80
|
|
|
78
81
|
except Exception as e:
|
|
79
82
|
print(f"Failed to load root cert from Secrets Manager: {e}", file=sys.stderr)
|
|
@@ -81,16 +84,17 @@ def write_root_cert(config: ServerConfig):
|
|
|
81
84
|
|
|
82
85
|
|
|
83
86
|
@asynccontextmanager
|
|
84
|
-
async def app_lifespan(server: FastMCP
|
|
87
|
+
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
|
|
88
|
+
global CONFIG
|
|
85
89
|
# Skip database connection for testing if YUGABYTEDB_URL is not set
|
|
86
|
-
if not
|
|
90
|
+
if not CONFIG or not CONFIG.yugabytedb_url:
|
|
87
91
|
print("YUGABYTEDB_URL is not set, running in demo mode without database connection", file=sys.stderr)
|
|
88
92
|
yield AppContext(conn=None)
|
|
89
93
|
return
|
|
90
94
|
|
|
91
95
|
print("Connecting to database...", file=sys.stderr)
|
|
92
|
-
database_url =
|
|
93
|
-
cert_path = write_root_cert(
|
|
96
|
+
database_url = CONFIG.yugabytedb_url
|
|
97
|
+
cert_path = write_root_cert()
|
|
94
98
|
if cert_path and "sslrootcert" not in database_url:
|
|
95
99
|
database_url += f" sslrootcert={cert_path}"
|
|
96
100
|
conn = psycopg2.connect(database_url)
|
|
@@ -106,8 +110,6 @@ def summarize_database(ctx: Context, schema: str = "public") -> List[Dict[str, A
|
|
|
106
110
|
Summarize the database: list tables with schema and row counts.
|
|
107
111
|
"""
|
|
108
112
|
summary = []
|
|
109
|
-
# Get config from context
|
|
110
|
-
config = ctx.request_context.lifespan_context.config
|
|
111
113
|
conn = ctx.request_context.lifespan_context.conn
|
|
112
114
|
|
|
113
115
|
# If no database connection, return demo data
|
|
@@ -227,13 +229,13 @@ def parse_config() -> ServerConfig:
|
|
|
227
229
|
|
|
228
230
|
|
|
229
231
|
class YugabyteDBMCPServer:
|
|
230
|
-
def __init__(self
|
|
231
|
-
|
|
232
|
+
def __init__(self):
|
|
233
|
+
global CONFIG
|
|
232
234
|
self.mcp = FastMCP(
|
|
233
235
|
"yugabytedb-mcp",
|
|
234
|
-
lifespan=
|
|
236
|
+
lifespan=app_lifespan,
|
|
235
237
|
json_response=True,
|
|
236
|
-
stateless_http=
|
|
238
|
+
stateless_http=CONFIG.stateless_http if CONFIG else False,
|
|
237
239
|
)
|
|
238
240
|
|
|
239
241
|
self._register_tools()
|
|
@@ -243,7 +245,7 @@ class YugabyteDBMCPServer:
|
|
|
243
245
|
self.mcp.add_tool(run_read_only_query)
|
|
244
246
|
|
|
245
247
|
def run(self, host="0.0.0.0", port=8000):
|
|
246
|
-
if
|
|
248
|
+
if CONFIG and CONFIG.transport == "http":
|
|
247
249
|
self._run_http(host, port)
|
|
248
250
|
else:
|
|
249
251
|
self.mcp.run(transport="stdio")
|
|
@@ -268,11 +270,12 @@ class YugabyteDBMCPServer:
|
|
|
268
270
|
|
|
269
271
|
if __name__ == "__main__":
|
|
270
272
|
CONFIG = parse_config()
|
|
271
|
-
server = YugabyteDBMCPServer(
|
|
273
|
+
server = YugabyteDBMCPServer()
|
|
272
274
|
server.run()
|
|
273
275
|
|
|
274
276
|
|
|
275
277
|
def main():
|
|
278
|
+
global CONFIG
|
|
276
279
|
CONFIG = parse_config()
|
|
277
|
-
server = YugabyteDBMCPServer(
|
|
280
|
+
server = YugabyteDBMCPServer()
|
|
278
281
|
server.run()
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
server.py,sha256=bc2juN3YT-P6yla0kglOyxeEAvpM4mcAqz_ySgUzwDc,9567
|
|
2
|
-
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
3
|
-
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info/METADATA,sha256=RHm9yfD5pPEv1VWSW4RinmmfCxSY9hy-vVxCbDI7bqY,9655
|
|
4
|
-
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
5
|
-
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info/entry_points.txt,sha256=0qR8G3C-z5Jb99iC45ysBZsnVGYgPefWGinP2Bhq9hE,54
|
|
6
|
-
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info/top_level.txt,sha256=StKOSmRhvWS5IPcvhsDRbtxUTEofJgYFGOu5AAJdSWo,7
|
|
7
|
-
iflow_mcp_yugabyte_yugabytedb_mcp_server-1.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|