Skip to main content

kx-mcp-kdbx — KDB-X backend

The KDB-X backend extension (bundle) for the KX MCP composition container. It exposes SQL query, schema-discovery, and vector/hybrid-search capability over qIPC via PyKX, and is packaged as a standard container bundle: a package exposing build_server() -> FastMCP, mounted under the kdbx namespace.

It is mount-only: the composition container (kx-mcp-core) owns transport/host/port, so the bundle has no standalone server of its own — build_server() just returns a configured FastMCP to mount(). Run it via the container: uv run kx-mcp --bundles kdbx.

The bundle connects to a KDB-X service listening on a host and port reachable from the MCP server. It relies on the KDB-X SQL module through its compatibility interface (.s); see setup.

Semantic metadata is a first-class backend capability. When the target host loads the optional aimeta module, agents receive annotated table, column, reference, and public-function semantics through the versioned schema://kdbx/metadata/v1 contract. Without aimeta, the same resources degrade cleanly to native table/column discovery. Prefer tables://kdbx/{table} or kdbx_get_table_metadata and functions://kdbx/{function} for token-efficient lookups. The administrative kdbx_refresh_metadata tool is route-only under the default unset authz posture; configured capability authz must grant admin on the kdbx namespace (kdbx.metadata is the recorded resource convention). See Semantic metadata with aimeta.

Table of contents

Supported environments

Primary OS KDB-X MCP Server UV/NPX
Mac ✅ Local ✅ Local ✅ Local
Linux ✅ Local ✅ Local ✅ Local
WSL ✅ Local ✅ Local ✅ Local
Windows ⚠️ WSL / Remote ✅ Local ✅ Local
  • KDB-X: Mac/Linux/WSL only (no native Windows support — run it under WSL or connect to a remote Linux host). The MCP server can still run natively on Windows and connect to a remote/WSL KDB-X.

Prerequisites

  • A valid PyKX / KDB-X license. The backend runs PyKX in licensed mode (PYKX_LICENSED=true); without a valid license import pykx fails and the bundle is disabled. Set QLIC to your license directory (containing a valid kc.lic). A valid license has not expired and carries the feature flags pykx/py and embedq/eq — see the PyKX licensing notes. KDB-X can be installed via the KDB-X public preview.
  • A reachable KDB-X service on a host and port the MCP server can reach, with the SQL module initialized (and, for search, the AI libraries — see setup). The bundle's default is 127.0.0.1:5010 (note: on macOS, :5000 is taken by Control Center / AirPlay Receiver, where a qIPC connect times out — hence :5010).
  • UV to run the server.

The license, qIPC reachability, and initialized .s interface are mount-time hard requirements — a failure disables the kdbx bundle while the parent keeps serving healthy ones. .ai is different: it's a feature gate, so without it the bundle still mounts but omits its search tools, and embedding/model configuration only becomes a prerequisite once those tools are actually called. The sample hosts in examples/ demonstrate a complete setup, but they aren't a runtime dependency.

KDB-X setup

Start a KDB-X service and load the SQL interface (and, for search, the AI libraries):

q -p 5010
.ai:use`kx.ai     // AI libraries (optional — enables similarity/hybrid search)
.s.init[]         // populate the .s SQL interface used by kdbx_run_sql_query

The SQL module uses a special compatibility integration: the installer places s.k_ on the q runtime path, preserving .s.init[] instead of requiring the normal module-framework load syntax.

A ready-made host for smoke testing lives at examples/host.q (loads .s + .ai, seeds annotated instruments and trades tables, listens on :5010):

q examples/host.q

Semantic metadata with aimeta

aimeta is optional. When the target host loads it, the backend reads its document over the existing qIPC connection and enriches discovery with descriptions, semantic types, foreign references, reference vocabularies, public function signatures, and declared table dependencies. Without aimeta—or when its document is invalid—the backend stays available and falls back to native tables[]/meta introspection.

Supported: aimeta v0.2.x–v0.3.x (schemaVersion 2–3). Each document is validated against the schema of its own version. A function's informative @authorize grant (schemaVersion 3) appears as authorize on its function entry. Any other schemaVersion falls back to native introspection with annotationStatus: "unsupported_schema".

The agent-facing shape is the backend's own stable metadata contract v1, not raw aimeta. Every response points to schema://kdbx/metadata/v1 and reports a richness tier, annotationStatus, and source. Tier 1 is native/degraded, Tier 2 is a valid basic aimeta document, and Tier 3 contains useful semantic annotations. Use tables://kdbx/{table} and functions://kdbx/{function} for token-efficient lookups.

First install aimeta and its runtime modules on the host by following its canonical installation guide (pinned to v0.3.0, the newest supported release). The module must resolve as kx/aimeta under $QPATH (normally ~/.kx/mod/kx/aimeta). Then enable it after defining the host's tables and functions:

aimeta:use`kx.aimeta;
aimeta[`init][];

The module and annotations are host-owned; kx-mcp-kdbx neither bundles nor requires aimeta.

Using the AI search tools

The kdbx_similarity_search and kdbx_hybrid_search tools require:

  • KDB-X version 0.1.2 or greater, and
  • the AI libraries loaded: .ai:use\kx.ai`

When the AI libraries are not present, the backend starts cleanly with only the SQL tool registered (feature-gated at registration time). When they are present, each search call still needs a table-specific embedding/model row and the corresponding table data expected by that configuration.

Configuration

The backend owns the KDBX_DB_* environment prefix. The container CLI does not carry backend config; configure the backend via env vars or .env. Transport/host/port are not the backend's concern — they belong to the container (KX_MCP_*), since the backend is mount-only.

Setting Env var Default Notes
Host KDBX_DB_HOST 127.0.0.1 KDB-X hostname or IP
Port KDBX_DB_PORT 5010 qIPC port
Username KDBX_DB_USERNAME (empty)
Password KDBX_DB_PASSWORD (empty) Backend connection credential; use this or KDBX_DB_PASSWORD_FILE, not both
TLS KDBX_DB_TLS false See TLS note below
Timeout KDBX_DB_TIMEOUT 1 Connect timeout (seconds)
Retry KDBX_DB_RETRY 2 Retries after a failed connect, so attempts = retry + 1 (0 = one attempt, 2 = up to three). Also applies when a cached handle is found dead and reopened. Must be >= 0
Embeddings CSV KDBX_DB_EMBEDDING_CSV_PATH (packaged utils/embeddings.csv) Per-table embedding config
Distance metric KDBX_DB_METRIC CS CS, L2, IP
Default k KDBX_DB_K 5 Default neighbours returned
aimeta cache TTL KDBX_DB_AIMETA_CACHE_TTL 300 Seconds to cache metadata (including absence); 0 disables caching
Identity assertion KDBX_DB_ASSERT_IDENTITY false Opt-in identity propagation — see below
Capability check (PEP-1) KX_MCP_AUTHZ=kdbx_rbac (container env) (unset = route-only) Container-side capability gates on the SQL tool (query / kdbx.sql) and metadata refresh (admin / kdbx.metadata). Setting KX_MCP_AUTHZ=kdbx_rbac routes both decorators to q .kx.auth over a capability grant set, distinct from the q-side data gate (PEP-2). Requires KDBX_DB_ASSERT_IDENTITY so a principal is bound. Unset leaves both tools route-only.
Data gate (PEP-2) KDBX_DB_DATA_GATE false A container-side explicit consult of the q data gate: before a query runs, the tool asks .kx.auth.entitled (one round-trip, on the bound per-principal handle) about data.<table> resources for the physical tables the query references, then maps the allowed resource paths back to table names. It acts on the verdict — allow, structured permission_denied, or scope-down: the table listing is filtered to the entitled subset; a partially-entitled SQL query gets a denial naming the entitled tables so the agent re-scopes (SQL is never rewritten). Requires KDBX_DB_ASSERT_IDENTITY=true (config-validated) and a kx.auth module that ships entitled (pre-flight-checked). Off = data gating (if any) happens only via a host-side .s.e wrap, not this seam.
Password file KDBX_DB_PASSWORD_FILE (empty) Read the service-account password from a file (overrides KDBX_DB_PASSWORD) — for K8s/Docker mounted secrets

Resolution order: env vars > .env file > defaults.

These defaults are a local-development posture: loopback, plaintext qIPC, empty connection credentials, identity assertion off, and data/capability gates off. With assertion off, the username/password simply identify the single backend connection; switch it on and they instead identify the trusted service account permitted to assert callers. The built-in Python static capability adapter is independent of kx.auth — only KX_MCP_AUTHZ=kdbx_rbac and KDBX_DB_DATA_GATE=true require the q authorization module and an asserted principal.

TLS. Enable with KDBX_DB_TLS=true. This requires your KDB-X database to be set up for TLS (see the kdb+ SSL/TLS guide). For self-signed certs, point KX_SSL_CA_CERT_FILE at the CA cert; for local development you can bypass verification with KX_SSL_VERIFY_SERVER=NO.

Identity assertion (multi-principal)

In the multi-principal posture, the container can propagate the validated inbound principal to plain kdb+ so q-side permission functions can gate on who is calling. qIPC has no bearer concept, so identity is asserted, not exchanged: the container ferries the structured fields plus raw claims, connects as a trusted service account, and calls .kx.auth.bind[principal] — none of it touches a JWT, crypto, or OAuth in q. q's .kx.auth.promote then extracts groups/tenant and types the principal, and because promotion lives q-side, the qIPC and HTTP paths share it. See the auth guide § plain kdb-x for the full walkthrough.

Enable it: set KDBX_DB_ASSERT_IDENTITY=true (default off). Off behaves exactly as the single-principal posture (no bind), so a vanilla kdb+ is unaffected. When on:

  1. Install and load the kx.auth KDB-X module on your KDB-X process. The module is not part of this repository — it and its peer policy engine kx.rbac are maintained in KxSystems/kx-auth. Put both on the q module path once:
    just install-modules                                        # fetches the pinned tag
    just kx_auth_src=/path/to/kx-auth install-modules           # ...or symlink a local checkout
    
    Without just or a clone of this repo, install them from a kx-auth release:
    KX_AUTH_VERSION=v0.5.0
    tmp="$(mktemp -d)"
    curl -fsSL "https://github.com/KxSystems/kx-auth/archive/refs/tags/${KX_AUTH_VERSION}.tar.gz" \
      | tar -xz -C "$tmp"
    mkdir -p ~/.kx/mod/kx
    cp -R "$tmp"/*/modules/kx/. ~/.kx/mod/kx/      # installs kx/auth and kx/rbac
    rm -rf "$tmp"
    export QPATH="$QPATH:$HOME/.kx/mod"            # add to your shell profile to persist
    
    Check that both resolve:
    echo 'r:(99h=type @[use;`kx.auth;{`err}]) and 99h=type @[use;`kx.rbac;{`err}]; -1 $[r;"ok";"FAILED"]; exit 0' | q -q
    
    Then, on the host:
    .kx.auth:use`kx.auth;                                       / MUST bind to the global `.kx.auth`
    .kx.rbac:use`kx.rbac;                                       / the peer policy engine
    .kx.auth.configure[(`kxmcp;"service-account-pw")];          / dev/reference verifier; see below
    .kx.auth.setClaims[(enlist `groups)!enlist "realm_access.roles"]; / where to read groups (host owns it)
    .kx.auth.setLoginGroups[(enlist `kxmcp)!enlist `superUsers]; / the svc LOGIN's own groups
    .kx.rbac.grant[`superUsers; `assert; `kx.identity];         / who may assert an identity
    .kx.rbac.grant[`trader;     `read;   `data.trades];         / ...and who may read what
    .kx.auth.setPolicy .kx.rbac.policy[];                       / install the engine (required)
    .kx.auth.activate[];                                        / wire .z.pw / .z.po / .z.pc (qIPC)
    / .kx.auth.activateHttp[];                                  / OPTIONAL: wire .z.ph / .z.pp (thin HTTP)
    
    kx.auth exposes bind / current / valid / require, a default-deny data-level authorization seam (setPolicy[fn] installs the decision function, either (principal;action;resource) -> 1b or the context-aware (principal;action;resources;ctx) form; authorize[action;resource] enforces it), and setClaims to point promotion at the right claim path (default search groups→realm_access.roles→roles). kx.rbac is the standard decision function, so you don't hand-roll one — one table of (group;action;resource) grants, with no verb subsumption (grant each verb explicitly). bind itself consults the same policy — the connecting login needs an `assert grant on `kx.identity or every assertion is refused. A kdb+ login carries no IdP groups, which is what setLoginGroups supplies. The eager pre-flight verifies .kx.auth.bind is defined and disables this bundle if not.
  2. Configure one credential source on each side. On the MCP side, set KDBX_DB_USERNAME plus either KDBX_DB_PASSWORD or KDBX_DB_PASSWORD_FILE (the file wins; a mounted secret is preferred in production). On q, either use .kx.auth.configure as shown for a self-contained development/reference host, or keep the platform's existing login verifier (-U password file or host-defined .z.pw). These are alternatives, not cumulative copies of the secret. Production deployments should normally preserve platform q authentication and mount the MCP-side password as a secret. The service-account credentials are the highest-trust secret here—holding them lets the container attempt identity assertion, while the policy's explicit `assert/`kx.identity grant determines whether q accepts it.
  3. TLS: strongly recommended (the service-account credential + asserted principal otherwise cross qIPC in cleartext). The pre-flight only warns when TLS is off (keeps the dev/loopback path open); treat TLS as required for any production deployment.

Permission-check functions read .kx.auth.current[] (or call .kx.auth.require[] / .kx.auth.authorize); q's promote gives the principal a first-class groups symbol vector (extracted from the configured claim path), so a policy gates on group membership — key on the promoted fields, not raw claims. A q-side denial surfaces to the agent as a structured permission_denied. To exercise the ferry + bind handshake from a shell without the container, use kx auth assert.

Configure embeddings

To use similarity search you configure an embedding model per table. Two providers ship ready to use (OpenAI and SentenceTransformers); add your own as needed:

  1. Add the provider dependency to this package's pyproject.toml (the optional embedding extras).
  2. Set any required API keys (e.g. OPENAI_API_KEY).
  3. Register a provider — src/kx_mcp_kdbx/utils/embeddings.py defines the EmbeddingProvider base class; subclass it and decorate with @register_provider. Use the OpenAI / SentenceTransformers implementations as templates.
  4. Map tables to models in src/kx_mcp_kdbx/utils/embeddings.csv — the provider name there must match a registered provider.

Running the backend

The backend is mount-only — run it through the container (there is no standalone mcp-server):

# Run the kdb-x backend via the container:
uv run kx-mcp --bundles kdbx

# Point it at a different KDB-X endpoint with the backend's own env prefix:
KDBX_DB_HOST=kdb-eu KDBX_DB_PORT=5011 uv run kx-mcp --bundles kdbx

The bundle registers bare primitive names (run_sql_query, …); the kdbx_ qualifier is supplied by the container's mount(namespace="kdbx").

kdbx_refresh_metadata asks aimeta to reload a recompiled document and refreshes this mounted backend's cache. It carries @authorize(action="admin", resource="kdbx.metadata"): authz unset keeps the single-principal route-only posture, while configured static or kdb-x RBAC must grant the admin capability explicitly.

Metadata contract maintenance: contract v1 is a public interface. Any projection change must update the packaged JSON Schema, resource/tool examples and descriptions, contract tests, and the changelog in the same change. An incompatible shape requires a new contract version and URI.

Troubleshooting

  • Failed to import pykx — no valid license. Check QLIC points to a directory containing a valid kc.lic with the required feature flags; update to the latest KDB-X if it has expired.
  • Connection error / bundle disabled — the KDB-X service is not reachable on the configured host:port (default 127.0.0.1:5010). The bundle logs a clear connectivity error and is not mounted; the parent continues serving other healthy bundles.
  • SQL interface not loaded — run .s.init[] on the host.
  • Missing AI search tools — confirm KDB-X ≥ 0.1.2 and .ai:use\kx.ai`; check the server logs for registration messages.
  • Tier 1 metadata — install/load kx.aimeta, annotate the host source, and compile it. The server log's annotationStatus distinguishes missing, empty, invalid, and failed metadata.

Metadata

Release files for kx-mcp-kdbx 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for kx-mcp-kdbx 0.5.0
File Interpreter ABI Platform
kx_mcp_kdbx-0.5.0-py3-none-any.whl Python 3 none any Details

Release files / kx_mcp_kdbx-0.5.0-py3-none-any.whl

Download URL kx_mcp_kdbx-0.5.0-py3-none-any.whl
Size 81.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5433b52bb22607ad5a5b010e17a417fbf9d1b4ff8f23dbb55fe8158095f1d7af
BLAKE2b-256 checksum
How to use checksums
06e5aefc4f460d9c572ca8c6cc8e6b628bcd98f070b65d8f0c6080327d43655e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.5.0 This release

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page