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.
4.6 KiB
xmcp
Turns any OpenAPI document (JSON or YAML) into a ready-to-run MCP server using FastMCP. 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)
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)
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:
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:
{
"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
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
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:
API_BASE_URL, if set.- The first entry of the spec's
serverslist. - 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. - 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:
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 -> slim runtime, non-root)
.gitea/workflows/build.yaml release pipeline (tag push -> docker build & push)