This reverts commit 9185b5fa05 while
preserving the build work done afterwards: setuptools-scm versioning with
uv cache keys, the git-enabled Docker builder (full checkout, credentials
excluded), and full-history/tag checkout in CI.
Configuration returns to environment variables with a single OpenAPI
document; .env.example and the env-based Docker/Compose setup are
restored.
Add uv cache-keys for the commit and tags so the editable install is
refreshed when the git state changes, and copy the complete checkout
(with .git) into the builder so setuptools-scm sees a clean worktree.
Previously the partial copy made git describe report a dirty tree and
tagged Docker builds got a development version suffix.
Replace hatchling and the static version with setuptools + setuptools-scm,
following Kaya's pre-uv packaging setup: dynamic version, generated
_version.py, and release/* tag prefix. The Docker builder now includes git
and the .git directory so the image is versioned from the checkout. CI
checks out full history and tags for the tag-triggered builds.
Configuration moves from environment variables to a YAML/JSON file,
located via --config, XMCP_CONFIG, or /etc/xmcp/xmcp.yml. String values
support ${VAR} interpolation for secrets.
One or more OpenAPI documents can be configured as specs[]; multiple
documents are mounted under unique namespaces and served on a single
transport, on one HTTP port. Tool filters, per-request API keys and
per-spec base URL/auth overrides live in the same file.
The Docker image no longer sets MCP_* defaults and expects the config
mounted at /etc/xmcp/xmcp.yml; the healthcheck reads the config.
Both Docker stages now use python:3.14-alpine3.24. Since there is no
combined uv image tag for python3.14-alpine3.24, the static uv binary is
copied from ghcr.io/astral-sh/uv:0.13.1 into the builder stage. Requires
Python 3.14, refreshes uv.lock, and updates the README badges.
The generated tool list is now logged at INFO (default) and each line
includes the tool's OpenAPI tags alongside its name. The list is only
skipped when LOG_LEVEL is above INFO.
Adds a comma-separated tag allowlist alongside the existing name
allowlist. When both are configured a tool must match both (AND).
Unknown names/tags are ignored with a startup warning, and a filter
that resolves to nothing exposes no tools rather than falling back to
all of them. Startup logs the active filters and the exposed tools.
When MCP_TOOLS_INCLUDED is set to a comma-separated list of tool names,
only those tools are exposed (tools/list and calls); unset or empty
exposes every generated tool. Unknown names are ignored with a startup
warning and the active selection is logged at startup.
With LOG_LEVEL=DEBUG, the generated tool list (name and first line of
description) is logged at startup for both transports. The listing is
skipped entirely at higher log levels.
When serving over HTTP, the key sent in the MCP_API_KEY_HEADER header
(default X-API-Key) is used as the API credential for that request and
takes precedence over the static API_TOKEN. The key is formatted with
API_AUTH_HEADER/API_AUTH_SCHEME and the source header is stripped from
the outgoing API request. Implemented as an httpx2 auth flow over
FastMCP's request context, so concurrent clients stay isolated.
Both build and runtime stages now use python3.13-alpine3.23 (uv image
for the builder, python image for the runtime), with BusyBox
addgroup/adduser for the non-root xmcp user. All native dependencies
ship musllinux wheels, so no compiler is needed in the builder.
Gitea Actions workflow triggered by release/* tags: builds the image
with the hostinger runner and pushes
gitea.woggioni.net/woggioni/xmcp:<version> and :latest.
Also document the release process in the README.
MCP server generated from an OpenAPI document with FastMCP v4.
Configurable via env vars (spec URL/file, transport stdio/http,
host, port, API token), built with uv, multi-stage Dockerfile
with non-root runtime.