Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MCP with Codex and Claude (Keycloak, Polaris)

Goal

Run SQE as a remote Model Context Protocol server over Streamable HTTP, backed by real Keycloak users, Apache Polaris and RustFS, and connect Codex or Claude Code to it. SQE is an OAuth resource server, not a credential proxy: the caller’s bearer becomes the same per-user session Flight SQL uses and travels to the catalog and storage unchanged, so an assistant reads exactly what its human can read.

adminuser/testuser bearer -> SQE MCP -> SQE Session -> Polaris -> RustFS

The quickstart is read-only by default. An explicit --writes mode proves that the OAuth scope, the SQE role and source-data authorization stay three independent gates.

Components

It reuses the polaris-keycloak-user-token stack instead of cloning it; only the MCP configuration, a Compose overlay, the tests and the client templates live here.

PieceRole
KeycloakIdentity provider. adminuser (data_writer) and testuser (reader only). sqe:mcp:read is a default client scope, sqe:mcp:write optional.
Apache PolarisIceberg REST catalog; authorizes every read and write with the user’s token.
RustFSS3-compatible object store holding the warehouse.
sqeThe engine, with /mcp on its existing web listener (http://localhost:19091/mcp).

Configuration

sqe.toml enables MCP with port = 0, so it shares the web/health port. The Compose overlay replaces only sqe.toml, exposes that port and carries the one runtime switch, SQE_MCP__ALLOW_WRITES. Storage keys and demo passwords arrive as SQE_<SECTION>__<KEY> environment overrides, so no secret is in the file. Every relaxed check is marked # DEV ONLY:.

SQE serves RFC 9728 protected-resource metadata and bearer challenges. The read-only SQL tool is gated by SQE’s own parser as an allow-list. Responses are capped at 100 rows and 256 KiB for predictable model context.

cd quickstart/mcp
cp .env.example .env
./run.sh --check       # read-only end-to-end tests
./run.sh --writes      # expose and test execute_sql_change
./run.sh --down

To connect a client, mint a short-lived user token and point the client template at it; the token stays in an environment variable and never lands on disk:

export SQE_MCP_TOKEN="$(./mint-token.sh)"
./setup-client.sh claude     # or: ./setup-client.sh codex

In a real deployment, give Keycloak and SQE public HTTPS URLs and use the clients’ browser OAuth login instead of a pre-minted token.

The test

test-static.sh validates every script, JSON and TOML file and the merged Compose model without starting services. test-mcp.sh speaks raw JSON-RPC over HTTP, so the server is tested independently of either AI client. run.sh --check mints two real signed JWTs, seeds data through Flight SQL and runs the MCP assertions against the same SQE process. run.sh --writes additionally proves the writer succeeds and that a reader holding the same OAuth write scope still fails the SQE role gate: the scope alone grants nothing.

Output

==> checking OAuth protected-resource metadata
  ok resource identifier
  ok read scope advertised
==> checking authentication challenges
  ok missing bearer returns 401
  ok challenge points to RFC 9728 metadata
  ok invalid bearer rejected
==> checking MCP initialization and tools
  ok protocol version
  ok read-only SQL tool
  ok metadata tools
==> checking per-user identity and governed reads
  ok admin identity reaches MCP
  ok source data read through SQE
  ok write rejected by read-only parser gate
==> checking the opt-in write gate
  ok write tool advertised
  ok writer role can change source data
  ok reader denied despite OAuth write scope

==> checks: 14 passed, 0 failed