BACK_TO_PROJECTS
Python 3.11 MCP (Model Context Protocol) SQL Server Oracle AWS Athena pyodbc oracledb openpyxl uvx

QueryMesh MCP

A local MCP server that lets an AI-powered IDE query 26+ production databases in plain English — schema-aware NL2SQL for validating data assumptions while building features, without opening SSMS or DataGrip.

The Problem

Warehouse operations run on five different database systems across a dozen distribution centers — a voice-picking system, a WMS execution engine, an internal ops application, an order management platform, and an ETL archive in AWS Athena. Answering a simple operational question, or checking a data assumption while building a feature, meant knowing which of those systems actually held the answer, then writing correct SQL against it.

In practice that meant opening DataGrip or SSMS, remembering which database had what, writing SQL with the right schema prefixes and join patterns, and constantly cross-referencing documentation for table and column names that don't follow a consistent convention across systems. Only developers or analysts with deep SQL knowledge could answer ad-hoc questions — and even they were re-deriving the same schema knowledge over and over.

I also wanted this for my own AI-assisted development workflow: when an AI coding assistant is adding a feature that touches one of these systems, it's far more useful — and far safer — if it can check the actual schema and current data before writing code against it, instead of guessing at column names or trusting stale comments.

Solution Architecture

AI-Powered IDE — Plain-English Question

Operator or developer asks a question in the IDE chat. The AI assistant reads MCP tool descriptions at session start and decides when a database question requires calling the server.

QueryMesh MCP Server — Python / stdio JSON-RPC

Registers tools for discovery, query execution, schema docs, and Excel export. Silently pulls schema context, proposes a query, executes only after approval, and returns formatted results.

Guardrails — enforced before any query runs
Schema Context

get_schema tool serves markdown docs — table/column names, joins, known business logic

Read-Only Validation

Regex, word-boundary DML/DDL rejection before any query reaches a connection

Row Limiting

100-row default in chat, 50K max on Excel export — no accidental full-table dumps

Error Sanitization

Strips connection strings, passwords, IPs, and file paths from every error response

Connection Routing — 26+ databases
SQL Server — pyodbc

DB1 (picking/loading, 11 DCs) and DB3 (internal ops app — EOD reports, QC, fill rates)

Oracle — oracledb (thin mode)

DB2 (WMS execution, 12 DCs + shared schema), DB4 (order management), DB5 (perishables)

AWS Athena
AWS Athena — boto3

ETL archive — historical queries against S3-backed data lake tables

Formatted Results in Chat — or openpyxl Excel Export

Row-limited results render inline. On request, export_to_excel runs the full query (up to 50K rows) and writes an auto-fit .xlsx file.

What It Looks Like

The interaction happens entirely inside the IDE's chat panel. Schema lookups are invisible to the user — the AI calls get_schema internally, then shows only the proposed query and, after approval, the result. Mockup below is recreated for this write-up; data shown is placeholder.

Product UI — recreated for this write-up
IDE — Chat
how many pallets are on hold at DC-04?
calling get_schema("hold") — reading schema context

Proposed query — DB2 (shared, DC-04 schema)

SELECT COUNT(*) FROM DC04.INVENTORY_DETAIL
WHERE status_code = 'HOLD'
Approve Edit
count
0

0 pallets on hold at DC-04. Want this exported to Excel?

Key Design Decisions

Python over .NET. This was originally specced as a .NET console app — the natural choice given my day-to-day stack. I switched to Python for zero-compile iteration while shaping the tool surface, a much simpler uvx-based distribution story, and a better fit generally for text/data wrangling than for a compiled console app.

Schema-as-context, not live introspection. Rather than having the AI run INFORMATION_SCHEMA queries at request time, each database system has a comprehensive markdown doc served through the get_schema tool — built from existing SQL query files already in the production codebase, live schema introspection, application enum definitions, and known join patterns. This gives the AI things no schema query can: e.g. "holds live in the inventory detail table with status_code = 'HOLD'" or "fork work types: LD = letdown, PU = putaway, PS = pallet select, PM = pallet move" — business logic that only exists in institutional knowledge otherwise.

Read-only enforced at the protocol layer, not the database layer. Regex-based validation with word-boundary matching rejects every DML/DDL keyword before a query ever reaches a connection — INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, and friends. Keyword matches inside string literals are ignored, so a query filtering on a status value that happens to contain "delete" as a substring doesn't false-positive.

Two connection shapes for the same engine. DB2 runs as its own Oracle schema per warehouse on a shared RDS instance. The server supports both direct per-warehouse connections (no prefix) and a shared cross-schema connection that takes a schema prefix — so a query can target one DC directly or reach across DCs when the question calls for it.

validator.py — Read-Only Enforcement
FORBIDDEN = [
    "INSERT", "UPDATE", "DELETE", "DROP", "ALTER",
    "TRUNCATE", "CREATE", "EXEC", "MERGE", "GRANT",
]

def validate_read_only(sql: str) -> None:
    """Raise if sql contains a write/DDL keyword outside string literals."""
    stripped = _strip_string_literals(sql)  # avoid false positives on values
    for keyword in FORBIDDEN:
        pattern = rf"\b{keyword}\b"
        if re.search(pattern, stripped, re.IGNORECASE):
            raise ReadOnlyViolation(
                f"Query rejected — '{keyword}' is not permitted. "
                f"This server only supports read (SELECT) queries."
            )

Technical Stack

ComponentTechnologyPurpose
MCP ServerPython 3.11+ / mcp SDKJSON-RPC stdio transport, tool registration
SQL Serverpyodbc + ODBC Driver 17DB1 (picking/loading), DB3 (internal ops app)
Oracleoracledb (thin mode)DB2 (WMS execution), DB4 (order mgmt), DB5 (perishables)
AWS Athenaboto3ETL archive data
Excel ExportopenpyxlFormatted .xlsx output with auto-fit columns
ConfigYAMLConnection metadata; real credentials in a gitignored local file
Distributionuvx / pyproject.tomlZero-install launch from any IDE workspace

Database Coverage

SystemEngineConnectionsPurpose
DB1SQL Server11 DCsVoice-directed picking and loading assignments
DB2Oracle12 DCs + sharedWMS execution — forklift work, inventory, receiving, shipping
DB3SQL Server1Internal ops application — EOD reports, QC inspections, audits, fill rates
DB4Oracle1Order management — orders, customers, items
DB5Oracle1Perishables management
AthenaAWS1ETL archive — historical queries

MCP Tools Exposed

ToolParametersDescription
list_connectionsShows available databases — never exposes credentials
list_tablesconnection_nameTable discovery for a database
describe_tableconnection_name, table_nameColumn definitions and types
query_sql_serverconnection_name, sqlExecute read-only SQL (100-row default)
query_oracleconnection_name, sqlExecute read-only Oracle SQL
query_athenasqlExecute read-only Athena/Presto SQL
export_to_excelconnection_name, sql, filenameFull export up to 50K rows as .xlsx
get_schematopicReturns structured schema documentation
list_schemasLists available schema docs

Security Model

Project Structure

Project Layout
querymesh-mcp/
├── pyproject.toml              # Package config, uvx entry point
├── config.yaml                 # Connection metadata (committed)
├── config.local.yaml           # Real credentials (gitignored)
├── src/querymesh_mcp/
│   ├── server.py               # MCP server entry point
│   ├── config.py                # YAML config loading
│   ├── models.py                # Data models
│   ├── services/
│   │   ├── validator.py         # Read-only SQL enforcement
│   │   ├── limiter.py           # Row limit injection/capping
│   │   └── sanitizer.py         # Error message sanitization
│   ├── db/
│   │   ├── sqlserver.py         # pyodbc execution
│   │   ├── oracle.py            # oracledb thin mode execution
│   │   └── athena.py            # boto3 Athena execution
│   └── tools/
│       ├── connections.py       # Discovery tools
│       ├── query.py             # Query execution tools
│       ├── export.py            # Excel export tool
│       └── schema.py            # Schema documentation tools
└── schemas/
    ├── db1-schema.md            # Picking/loading tables
    ├── db2-schema.md            # 30+ WMS execution tables
    ├── db3-schema.md            # Internal ops app tables + enum reference
    └── db4-schema.md            # Order management tables

Results

Query turnaroundSeconds — down from minutes of tool-hopping and manual SQL
Database connections26+ across 4 engines (SQL Server, Oracle, Athena)
AccessibilityNon-SQL users can get operational answers directly, in plain English
Write riskZero — enforced at the protocol layer, before any query reaches a connection
DistributionUser-level MCP config — works from any IDE workspace, zero-install via uvx

My Role

Personal tool, built for my own workflow and shared with the broader ops/dev team. I designed and built the full system end to end.

Company, system, and database names have been anonymized. Architecture and technical details are accurate.