# xmcp Turns any [OpenAPI](https://spec.openapis.org/oas/latest.html) document (JSON or YAML) into a ready-to-run [MCP](https://modelcontextprotocol.io) server using [FastMCP](https://gofastmcp.com). One MCP tool is generated per API operation, and requests to the target API are authenticated with a token supplied via environment variables. Everything is configured through environment variables: the spec location, the transport (stdio or streamable HTTP), the listen host/port, and the API token. ## Quick start ### Docker Compose (HTTP) ```sh cp .env.example .env # then edit OPENAPI_SPEC_URL / API_TOKEN etc. docker compose up --build ``` The MCP endpoint is served at `http://localhost:8000/mcp`. ### Docker (HTTP) ```sh docker build -t xmcp . docker run --rm -p 8000:8000 \ -e OPENAPI_SPEC_URL=https://petstore3.swagger.io/api/v3/openapi.json \ xmcp ``` ### Docker (stdio) For stdio the container must run with an open stdin (`-i`); logs go to stderr, so stdout stays protocol-clean: ```sh docker run -i --rm \ -e MCP_TRANSPORT=stdio \ -e OPENAPI_SPEC_URL=https://petstore3.swagger.io/api/v3/openapi.json \ xmcp ``` Example MCP client configuration for the stdio container: ```json { "mcpServers": { "petstore": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "MCP_TRANSPORT=stdio", "-e", "OPENAPI_SPEC_URL=https://petstore3.swagger.io/api/v3/openapi.json", "xmcp:latest" ] } } } ``` ### Local development with uv ```sh uv sync OPENAPI_SPEC_URL=https://petstore3.swagger.io/api/v3/openapi.json uv run xmcp # equivalent: uv run python -m xmcp ``` ## Configuration | Variable | Required | Default | Description | | --- | --- | --- | --- | | `OPENAPI_SPEC_URL` | yes | – | `http(s)://` URL or local file path of the OpenAPI document (JSON or YAML) | | `OPENAPI_SPEC_TOKEN` | no | – | Bearer token used only when fetching a remote spec document | | `API_BASE_URL` | no | spec `servers[0].url` | Overrides the target API base URL | | `API_TOKEN` | no | – | Credential sent to the target API on every tool call | | `API_AUTH_HEADER` | no | `Authorization` | Header used to carry the token (e.g. `X-API-Key`) | | `API_AUTH_SCHEME` | no | `Bearer` | Scheme prefix; set to an empty value to send the raw token | | `MCP_TRANSPORT` | no | `stdio` (`http` in the image) | `stdio` or `http` (`streamable-http` is an alias) | | `MCP_HOST` | no | `0.0.0.0` | Listen host for the HTTP transport | | `MCP_PORT` | no | `8000` | Listen port for the HTTP transport | | `MCP_SERVER_NAME` | no | spec `info.title` | Name advertised by the MCP server | | `HTTP_TIMEOUT` | no | `30` | Timeout in seconds for spec fetch and API calls | | `LOG_LEVEL` | no | `INFO` | Python log level | Authentication examples: for a bearer token leave the defaults; for an API key header use `API_AUTH_HEADER=X-API-Key` with `API_AUTH_SCHEME` set to an empty string. ### Serving a local spec file ```sh docker run -i --rm \ -v "$PWD/openapi.json:/spec/openapi.json:ro" \ -e OPENAPI_SPEC_URL=/spec/openapi.json \ xmcp ``` If the document has no absolute `servers[0].url` (for example it only contains a relative path such as `/v1`), set `API_BASE_URL` to the API root explicitly. ### Base URL resolution The base URL for generated tools is chosen as follows: 1. `API_BASE_URL`, if set. 2. The first entry of the spec's `servers` list. 3. If that server URL is relative (e.g. `/api/v3`) and the spec was fetched over HTTP(S), it is resolved against the spec's origin. 4. Otherwise startup fails with a message asking for `API_BASE_URL`. ## Security notes - The API token is attached to every request the generated tools make to the target API. It is never logged. - The MCP HTTP endpoint (`/mcp`) itself is **not** authenticated. Run it on a private network or behind a reverse proxy / gateway that enforces authentication before exposing it publicly. ## Releasing Pushing a `release/*` tag triggers the Gitea Actions pipeline, which builds the Docker image and pushes it to the Gitea container registry: ```sh git tag release/0.1.0 git push origin release/0.1.0 ``` This publishes `gitea.woggioni.net/woggioni/xmcp:0.1.0` and `gitea.woggioni.net/woggioni/xmcp:latest`. ## Project layout ``` src/xmcp/config.py env var parsing and validation src/xmcp/spec.py spec loading, base URL resolution, API client src/xmcp/server.py FastMCP server construction and entrypoint Dockerfile multi-stage build (uv builder -> alpine runtime, non-root) .gitea/workflows/build.yaml release pipeline (tag push -> docker build & push) ```