Skip to content

Commit 0165281

Browse files
committed
Mask credentials in UI, add MySQL support, add screenshots to README
- Config.db_display_name property: shows db type + name without credentials - Overview page shows masked DB identifier instead of raw connection URL - Settings page: Connection URL field uses password mask (reveal on focus) - Add pymysql to requirements, MySQL to supported databases in README - Add admin UI screenshots (overview, sandbox, settings) to README
1 parent e771628 commit 0165281

5 files changed

Lines changed: 32 additions & 3 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616

1717
---
1818

19+
## Screenshots
20+
21+
| Overview | Sandbox | Settings |
22+
|---|---|---|
23+
| ![Overview](docs/screenshots/overview.png) | ![Sandbox](docs/screenshots/sandbox.png) | ![Settings](docs/screenshots/settings.png) |
24+
1925
## What is this?
2026

2127
SQL Cortex MCP is a [Model Context Protocol](https://modelcontextprotocol.io) server that exposes SQL databases as tools for AI agents. It sits between your LLM and your database, providing:
@@ -158,6 +164,7 @@ Settings changed via the admin UI override environment variables at runtime and
158164
### Supported databases
159165

160166
- **SQLite** — built-in, no extra dependencies
167+
- **MySQL** — via `pymysql` (included in requirements)
161168
- **PostgreSQL** — via `psycopg2` (included in requirements)
162169

163170
## Development

app/config.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,27 @@ def load(cls) -> "Config":
118118
chat_history_limit=chat_history_limit,
119119
)
120120

121+
@property
122+
def db_display_name(self) -> str:
123+
"""Human-readable DB identifier without credentials."""
124+
from urllib.parse import urlparse
125+
126+
try:
127+
# Normalize: strip driver prefix (mysql+pymysql → mysql)
128+
url = self.db_url
129+
if ":///" in url:
130+
# SQLite-style: sqlite:///./data/dev.db
131+
return f"{self.db_type}{url.split('///')[-1]}"
132+
parsed = urlparse(url)
133+
host = parsed.hostname or ""
134+
port = f":{parsed.port}" if parsed.port else ""
135+
db_name = parsed.path.lstrip("/") if parsed.path else ""
136+
if host:
137+
return f"{self.db_type}{db_name}@{host}{port}"
138+
return self.db_type
139+
except Exception:
140+
return self.db_type
141+
121142
@property
122143
def db_type(self) -> str:
123144
url = self.db_url.lower()

app/web/routes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def index(request: Request):
4747
{
4848
"request": request,
4949
"mode": cfg.mode,
50-
"db_url": cfg.db_url,
50+
"db_url": cfg.db_display_name,
5151
},
5252
)
5353

app/web/templates/settings.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ <h2>Database</h2>
5757
<form method="post" action="/settings/db">
5858
<div class="form-group">
5959
<label class="field-label">Connection URL</label>
60-
<input type="text" name="db_url" value="{{ live_db_url }}" placeholder="sqlite:///./data/dev.db">
60+
<input type="password" name="db_url" value="{{ live_db_url }}" placeholder="sqlite:///./data/dev.db"
61+
onfocus="this.type='text'" onblur="this.type='password'">
6162
<span class="field-hint">Changing this requires server restart.</span>
6263
<div class="chips">
6364
<button type="button" class="chip" onclick="setDbTemplate('sqlite:///./data/dev.db')">SQLite</button>

tests/test_mcp_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,5 +252,5 @@ async def test_ui_routes_use_latest_runtime_config(
252252
monkeypatch.setattr(main_module, "registry", ToolRegistry())
253253
resp = await client.get("/")
254254
assert resp.status_code == 200
255-
assert "sqlite:///./runtime-refresh.db" in resp.text
255+
assert "SQLite" in resp.text and "runtime-refresh.db" in resp.text
256256
assert "execute" in resp.text

0 commit comments

Comments
 (0)