Skip to content

Running it

The server runs one of two ways: over stdio, started by one person’s agent on their own machine, or over Streamable HTTP, as a server a team shares. Either way it reads the same configuration, checks it and reaches every database before it answers anything.

It is a Python package on PyPI, each engine’s driver an extra (engines) — uvx --from 'semantic-warehouse-mcp[all]' semantic-warehouse-mcp … runs it without installing anything else, pip install 'semantic-warehouse-mcp[postgres]' installs it with one driver (Python 3.12 or newer: give uvx --python 3.12 when the default is older) — and a container image, which holds every driver. Context and skills read from a repository need the git program as well (2.31 or newer), which the image holds.

semantic-warehouse-mcp --config <file> [--check [--offline]]
semantic-warehouse-mcp --config <file> --http [--host 127.0.0.1] [--port 8000] [--path /mcp] [--token-env NAME]
semantic-warehouse-mcp import-dbt <configuration> [--strict] [--check] # the dbt importer: its own page
OptionDefaultWhat
--configSEMANTIC_WAREHOUSE_MCP_CONFIGthe configuration file
--checkcheck the configuration, reach every database, say what is served, and exit — 0 when fine, 2 with every problem
--offlinewith --check: reach no database and read no secret — for CI. Every model, vocabulary and readable rule is checked; a source table is held to readable but not looked up in the database
--httpserve over Streamable HTTP instead of stdio
--host127.0.0.1with --http: the address to listen on — 0.0.0.0 to be reached from other machines (a container)
--port8000with --http: the port
--path/mcpwith --http: the MCP endpoint’s path
--token-envwith --http: the environment variable holding the token every request must carry. Required unless --host is 127.0.0.1, localhost or ::1

The dbt importer is a command of its own, import-dbt.

The agent starts the server and talks to it through its standard input and output; nothing listens on the network. Getting started connects Claude Code this way.

Terminal window
export SWM_TOKEN=… # a long random value; agents send it, nothing else knows it
semantic-warehouse-mcp --config config.yaml --http --host 0.0.0.0 --port 8000 --token-env SWM_TOKEN
  • The token. Every request carries Authorization: Bearer <token>; a request without it, or with another, is answered 401 and reaches no tool. The token is read from the variable at start — its value is never written to a file, a log or an answer. A server that other machines can reach does not start without one; a variable named but not set stops it too, before any database is reached.
  • /health answers 200 with {"status": "ok", …} without a token, and reaches no database — a readiness probe that a database outage does not turn into restarts.
  • Stateless. Each request is answered on its own, one JSON answer each: any number of copies of the server can sit behind one address, and a client may reach any of them.

An agent connects with the endpoint and the header — Claude Code:

Terminal window
claude mcp add --transport http warehouse https://mcp.example.com/mcp --header "Authorization: Bearer $SWM_TOKEN"

A server reading context or skills from a repository keeps its copies in a temporary folder (TMPDIR, or /tmp) and removes them when it ends — at the end of its input, or stopped by SIGTERM. It says in its log (standard error) when it serves a new commit, and why it does not when a look for one goes wrong. Of the server’s environment only these reach git: PATH; behind a proxy, the usual variables (HTTPS_PROXY, HTTP_PROXY, ALL_PROXY, NO_PROXY, in either case) — for an https:// repository only, so a token never crosses a proxy in clear text; and a certificate authority’s (SSL_CERT_FILE, SSL_CERT_DIR, GIT_SSL_CAINFO, GIT_SSL_CAPATH).

An organisation’s MCP gateway (signing people in, deciding who sees which servers) usually reaches its backends with a key of its own: give that key to the server as its token. Who is asking stays at the gateway — the server answers the gateway, not the person.

The server’s container image holds the package and its locked dependencies and git, runs as user 10001 without rights, and works on a read-only root filesystem with a writable /tmp (where it keeps the copies of repositories). It serves over HTTP on port 8000, reading /config/config.yaml and the token from SWM_TOKEN:

Terminal window
docker run --read-only --tmpfs /tmp --cap-drop ALL \
-v "$PWD/my-config:/config:ro" -e SWM_TOKEN="$SWM_TOKEN" -e WAREHOUSE_PASSWORD="$WAREHOUSE_PASSWORD" \
-p 8000:8000 ghcr.io/alexeiveselov92/semantic-warehouse-mcp:<version>

Mount the configuration and everything it names (semantic models, vocabularies, context documents, a DuckDB file) at /config, read-only; pass each secret the configuration names by its variable. Arguments after the image replace the command — --config /config/other.yaml --http --host 0.0.0.0 --token-env OTHER. Images are published for each release, tagged with its version and commit (0.1.0, sha-1a2b3c4), never latest.