STABLE — Battle-tested in production. API is stable as of v2.0.
A sealed, retry-capable SQL Server connector for OpenClaw agents. Built on pymssql — no sqlcmd, no ODBC drivers, no system tools required.
- Multi-backend:
local(on-prem) andcloud(hosted) in one connector - Env-var driven default: set
SQL_DEFAULT_BACKENDin.env— no code change needed - Sealed transport:
execute()andquery()sealed via metaclass — subclasses cannot bypass parameterized queries - Retry with backoff: automatic retry (3x, 2s delay) on transient connection failures
- Operations:
execute()(INSERT/UPDATE/DELETE),query()(SELECT → list of dicts),scalar()(single value),ping()(health check) - Nothing hardcoded: all credentials from
.envonly
# Via ClawHub
clawhub install sql-connector
# Or pip
pip install clawbot-sql-connectorpip install pymssql python-dotenv
pymssqlbundles its own TDS driver. Nosqlcmd, no ODBC, no system packages.
Copy .env.example to .env and fill in your values:
cp .env.example .envExternal / ClawHub users (cloud SQL Server):
SQL_CLOUD_SERVER=your-server.database.windows.net
SQL_CLOUD_DATABASE=your_database
SQL_CLOUD_USER=your_user
SQL_CLOUD_PASSWORD=your_password
SQL_DEFAULT_BACKEND=cloud # optional, cloud is default for external usersLocal SQL Server (self-hosted, Oblio setup):
SQL_LOCAL_SERVER=10.0.0.110
SQL_LOCAL_DATABASE=Oblio_Memories
SQL_LOCAL_USER=oblio
SQL_LOCAL_PASSWORD=your_password
SQL_DEFAULT_BACKEND=local # override default to localfrom sql_connector import get_connector
db = get_connector() # uses SQL_DEFAULT_BACKEND from .env
# db = get_connector('cloud') # explicit
# db = get_connector('local') # explicit
# SELECT → list of dicts
rows = db.query(
"SELECT id, content FROM memory.Memories WHERE category = %s",
('facts',)
)
for row in rows:
print(row['content'])
# INSERT / UPDATE / DELETE → bool
ok = db.execute(
"UPDATE memory.Memories SET importance = %s WHERE id = %s",
(8, 42)
)
# Single value
count = db.scalar(
"SELECT COUNT(*) FROM memory.TaskQueue WHERE status = %s",
('pending',)
)
# Health check
if db.ping():
print("Connected")Always use %s placeholders. Never f-strings or string interpolation.
# ❌ WRONG — SQL injection risk
db.query(f"SELECT * FROM Memories WHERE category = '{category}'")
# ✅ CORRECT
db.query("SELECT * FROM Memories WHERE category = %s", (category,))The sealed metaclass prevents execute() and query() from being overridden in subclasses, so this is enforced by design.
Create a repository subclass for domain logic — don't subclass the transport:
from sql_connector import MSSQLConnector
class MemoryRepository(MSSQLConnector):
def get_recent_facts(self, limit: int = 10):
return self.query(
"SELECT TOP %s * FROM memory.Memories "
"WHERE category = %s ORDER BY created_at DESC",
(limit, 'facts')
)
repo = MemoryRepository('local')
facts = repo.get_recent_facts(5)from sql_connector import get_connector, SQLConnectionError, SQLQueryError
db = get_connector()
try:
rows = db.query("SELECT * FROM memory.Memories WHERE id = %s", (99999,))
except SQLConnectionError as e:
print(f"Connection failed (retry-eligible): {e}")
except SQLQueryError as e:
print(f"Query failed (do not retry): {e}")SQL_DEFAULT_BACKEND env var |
get_connector() connects to |
|---|---|
| Not set (external/ClawHub) | cloud |
cloud |
cloud |
local |
local (Oblio's .env sets this) |
This means:
- ClawHub users get
cloudby default (they have no local server) - Oblio's
.envsetsSQL_DEFAULT_BACKEND=localfor local-first operation - No code change needed — just
.envconfiguration
This skill is published to clawhub.ai as sql-connector.
Version policy: Stable releases only. We run this in production and publish when stable for 30+ days.
Factory. Returns MSSQLConnector for the given backend.
Execute SELECT. Returns rows as list of dicts.
Execute INSERT/UPDATE/DELETE. Returns True on success.
Execute query returning a single value.
Connectivity check. Returns True if connected.
v1.x compatibility factory.
MIT — see LICENSE.