Skip to main content
Glama
Newpaw

Alza Trade MCP server

by Newpaw

Alza Trade MCP server

A Python 3.12 MCP server covering all 9 endpoints in the Alza Trade API v2 Swagger. Built with FastMCP 3, httpx, uv, Docker and GitHub Actions. Remote clients use Streamable HTTP at /mcp; local clients can use stdio.

This is the Alza partner / marketplace API. It exposes listing metadata and partner orders. It does not expose retail product search, retail prices, customer shopping carts or checkout. A normal alza.cz login is not sufficient. This is an independent integration, not an official Alza product.

Quick start

git clone https://github.com/Newpaw/alzaMCP.git
cd alzaMCP
cp .env.example .env
# Edit .env: set ALZA_CLIENT_ID, ALZA_CLIENT_SECRET, and MCP_API_KEY.
docker compose up -d --build
curl http://127.0.0.1:8019/healthz
curl http://127.0.0.1:8019/readyz

Alternatively, use the published image without building locally:

docker compose pull
docker compose up -d --no-build

Image: ghcr.io/newpaw/alzamcp:latest. Release tags such as v0.1.0 produce image tags 0.1.0 and 0.1; every build also publishes sha-<full-commit-sha>. Pin a SHA tag or digest for controlled deployments.

Without Alza credentials the process still starts: /healthz is 200, /readyz is 503, MCP discovery works and upstream tools report a clear configuration error. This is useful for deployment checks; it does not mean live Alza data has been verified.

Related MCP server: weclapp-mcp-server

Authentication and configuration

Alza requires OAuth2 client credentials, with token endpoint https://identity.alza.cz/connect/token and scope marketplace_public. Obtain a client ID and secret with access to this API from Alza Trade. The server exchanges them using a form-encoded token request, caches the access token in memory and refreshes it before expiration. One 401 triggers one forced token renewal. Credentials and tokens are never exposed as tools, resources or response errors.

The MCP service key and the Alza client secret serve different purposes:

Variable

Default

Purpose

ALZA_CLIENT_ID

empty

Partner application client ID; set with its secret

ALZA_CLIENT_SECRET

empty

Partner application client secret

ALZA_SCOPE

marketplace_public

Upstream OAuth scope from the Swagger

ALZA_LANGUAGE

cs

Default Accept-Language; tools can override it

ALZA_BASE_URL

https://portalapi.alza.cz

Trusted upstream API URL; HTTPS required

ALZA_TOKEN_URL

https://identity.alza.cz/connect/token

Trusted identity URL; HTTPS required

ALZA_TIMEOUT_SECONDS

20

Per-operation HTTP timeout, including reads; connect timeout is 5 s

ALZA_MAX_RETRIES

2

Additional GET attempts on connection errors, 429, 502, 503, 504

ALZA_MAX_RESPONSE_BYTES

20000000

Maximum decoded API response size

MCP_HOST / MCP_PORT

0.0.0.0 / 8000

Container listener

MCP_TRANSPORT

http

http or stdio

MCP_API_KEY

empty

Optional bearer key for /mcp; required by the Komodo compose file

MCP_PUBLISHED_PORT

8019

Local compose loopback port

IMAGE_TAG

latest

Image tag used by compose

Settings can be supplied through environment variables or a local .env file. Never commit .env. Application secrets belong in Komodo's environment configuration, not in GitHub Actions. The container build does not need any Alza credentials.

Generate a service key:

uv run python -c 'import secrets; print(secrets.token_urlsafe(48))'

With MCP_API_KEY set, send Authorization: Bearer <key> on each MCP request. This is service-key authentication, not an OAuth authorization server. Interactive OAuth-only clients need authentication at an appropriate external gateway. Do not send the Alza client secret to an MCP client. Stdio runs locally and does not use the HTTP bearer key.

Supported tools

Responses retain the original Alza JSON structure, including pagination metadata. No fields are silently renamed or discarded. Date filters use RFC 3339 timestamps with an explicit offset, for example 2026-10-26T00:00:00+01:00. Each tool accepts an optional language override. Category identifiers must be positive; category filters accept 1–10 identifiers. Arrays are sent as repeated query parameters, matching OpenAPI's default form/explode query encoding.

MCP tool

Alza endpoint

Filters

get_languages

GET /v2/languages

language

get_categories

GET /v2/listings/categories

parent_id, language

get_category_tree

GET /v2/listings/categories/tree

parent_id, language

get_manufacturers

GET /v2/listings/manufacturers

language

get_parameters

GET /v2/listings/parameters

optional category_ids, language

get_seo_prefixes

GET /v2/listings/seo-prefixes

optional category_ids, language

get_listing_templates

GET /v2/listings/templates

required category_ids, language

get_orders

GET /v2/orders

creation dates, shipment departure dates, states, pagination, sort, language

get_unshipped_orders

GET /v2/orders/unshipped

shipment departure dates, language

get_orders parameters: created_from, created_to, shipment_departure_time_from, shipment_departure_time_to, states, page (default 1), page_size (default 50, max 500), sort_desc, sort_key, language. Call it repeatedly with successive pages when required; the server deliberately does not download all orders automatically. The Swagger does not declare the upstream default or minimum page number; this integration uses the conventional 1-based page number, which should be confirmed with your live account.

State values: 0 Pending, 1 LabelNotGenerated, 2 UnShipped, 3 Shipped, 4 Canceled, 5 CanceledAfterShipping, 6 Delivered, 7 Delayed, 8 DelayedRepeatedly, 9 ShippedLate, 10 CanceledLate, 11 DeliveredLate, 12 AwaitingCopayAmount, 13 AwaitingPreparation. Alza includes states 7 and 8 when filtering for state 1 or 2.

Sort keys: 0 Id, 1 Created, 2 State, 3 ShipmentDepartureTime.

get_parameters without a category filter may time out according to Alza's documentation. Narrow the filter rather than repeatedly downloading the complete collection. Templates omit unknown category identifiers, as documented by Alza. Very large responses fail explicitly at the configured size limit; no result is silently truncated.

The MCP resource alza://api-info describes capabilities without exposing configuration secrets. Every tool advertises read-only, non-destructive, idempotent annotations. All tools operate on the same partner identity configured on the server: deploy separate instances for different partners. This is not a multi-tenant user delegation system.

Connect an MCP client

Remote endpoint after the tunnel is configured: https://alzamcp.novopacky.com/mcp.

Example client configuration for clients supporting Streamable HTTP and custom headers (the exact configuration location varies):

{
  "mcpServers": {
    "alza": {
      "type": "http",
      "url": "https://alzamcp.novopacky.com/mcp",
      "headers": {"Authorization": "Bearer <MCP_API_KEY>"}
    }
  }
}

Python discovery and live API check:

import asyncio
import os
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport


async def main():
    transport = StreamableHttpTransport(
        "https://alzamcp.novopacky.com/mcp",
        headers={"Authorization": f"Bearer {os.environ['MCP_API_KEY']}"},
    )
    async with Client(transport) as client:
        print([tool.name for tool in await client.list_tools()])
        print(await client.call_tool("get_languages", {"language": "cs"}))
        print(await client.call_tool("get_orders", {"page": 1, "page_size": 10}))


asyncio.run(main())

For a local stdio client, set environment variables including MCP_TRANSPORT=stdio and run uv run --locked alza-mcp in the repository. A Docker stdio client can run docker run --rm -i --env-file .env -e MCP_TRANSPORT=stdio ghcr.io/newpaw/alzamcp:latest. Do not add -t; MCP stdio needs a clean byte stream.

Komodo and Cloudflare Tunnel

Use compose.komodo.yaml as the stack contents, select the target server and configure MCP_API_KEY, ALZA_CLIENT_ID and ALZA_CLIENT_SECRET in the stack environment. The external Docker network cloudflare must already exist, and the existing cloudflared connector must be attached to it. The service name / network alias is alza-mcp, listener port 8000. No host port is published.

Route the chosen hostname through the existing tunnel to http://alza-mcp:8000. TLS terminates at the tunnel edge. The application uses stateless Streamable HTTP with JSON responses, so there is no sticky session requirement. Proxy headers do not become application identity.

Cloudflare Zero Trust / Access is intentionally not implemented or provisioned by this project. There is no Cloudflare SDK, token, JWT validation, Access policy, OAuth gateway or CORS workaround in the application. The optional service bearer guard works independently. If an external Access policy is later added, configure the MCP client to satisfy that policy as well. A browser login redirect is not sufficient for a machine client. Check your client's support for Access service-token headers or an external OAuth gateway before enabling such a policy. Do not remove the service key until an equivalent access boundary is in place.

Operations and updates

  • GET /healthz: unauthenticated liveness, 200 when running. Docker health checks use this.

  • GET /readyz: unauthenticated configuration readiness, 200 with both Alza credentials or 503 without them. It does not contact Alza or prove that credentials work.

  • Verify upstream access by calling get_languages after configuring credentials.

  • Logs omit HTTP access logs and upstream response bodies. Never enable HTTP debug logging in production.

  • OAuth tokens live only in memory and are renewed after a process restart. There is no database, persistent token storage or customer-data cache.

  • Retry delay uses jitter and Retry-After (seconds or HTTP date), capped at 5 s. Only GET requests retry automatically. A token request is not retried; a subsequent tool call can retry it.

  • Up to 8 concurrent upstream operations are allowed. Each response is streamed into a bounded buffer and parsed only after completion. A multi-attempt request can take longer than one configured HTTP timeout; set the MCP client timeout accordingly (for example 120 s).

  • Runtime runs as UID/GID 10001, with a read-only filesystem, dropped Linux capabilities, no-new-privileges and a bounded temporary filesystem. Compose sets CPU and memory limits.

Pushes to main and version tags run lint, format checks, tests and Docker smoke tests before publishing AMD64/ARM64 images to GHCR. PRs only run checks. The workflow needs only the built-in GITHUB_TOKEN, with packages: write granted to the publish job. The package must be public for anonymous pulls; GitHub may initially create it as private even when the repository is public. If needed, change the package visibility in GitHub Packages settings or configure Komodo with a registry account authorized to pull it.

In Komodo, enable image polling and auto-update to redeploy latest after publication. For a manual update, pull and redeploy the stack. For rollback, set IMAGE_TAG=sha-<previous-good-commit> and redeploy; disable automatic image updates while pinned. Tag pushes alone do not guarantee a deployment if you run a pinned SHA.

docker compose logs --tail 100 alza-mcp
docker compose pull
docker compose up -d --no-build
# Roll back to a previously published SHA:
IMAGE_TAG=sha-<previous-good-commit> docker compose up -d --no-build

Development and verification

uv sync --locked
uv run --locked ruff check .
uv run --locked ruff format --check .
uv run --locked pytest -q
uv run --locked alza-mcp

The committed uv.lock fixes dependency versions and hashes. Build-time installation uses uv, never pip. The multi-stage image contains only the runtime environment, not uv or development tools. Dependabot tracks uv, Docker and Actions updates.

Tests cover every endpoint through an in-process MCP client, exact query/header encoding, token caching and concurrent refresh, 401 renewal, transient retries, sanitized errors, response-size limits, invalid inputs, health/readiness and actual HTTP JSON-RPC initialization with service-key enforcement. CI also boots the built non-root container before pushing it. These checks do not replace a live Alza account integration test.

The fetched Swagger snapshot is in spec/alza-v2.json; it is a reference contract owned by Alza, excluded from the runtime image. The coverage test fails when the snapshot and supported endpoint list diverge. To adopt upstream API changes, refresh the snapshot from https://portalapi.alza.cz/swagger/v2/swagger.json, review the diff, update tools/tests, and run all checks. The server does not download or execute a changing remote specification at startup.

Troubleshooting

Symptom

Check

/healthz is 200, /readyz is 503

Set both partner credentials in Komodo and redeploy

MCP HTTP 401

Send the MCP service key; this is separate from the Alza credentials

Tool reports OAuth 400/401

Check client ID, secret, grant permissions and marketplace_public scope

Tool reports API 403

Ask Alza to verify endpoint access for the partner client

HTML or redirect instead of MCP JSON

Inspect the external gateway / Access policy; do not disable TLS verification

Tunnel 502

Check container health, Docker network membership and http://alza-mcp:8000 origin

GHCR unauthorized

Make the package public or configure a Komodo registry pull credential

408, timeout or response-size limit

Narrow category/date filters and lower order page size

Empty orders on page 1

Confirm upstream pagination semantics and partner permissions with Alza

Sources

Application code is Apache-2.0 licensed, preserving the repository's original license. The upstream API contract and trademarks remain their respective owners' property.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides read-only MCP tools for weclapp Cloud ERP, letting AI assistants retrieve customers, sales orders, invoices, articles, quotations, recurring invoices, and opportunities.
    AGPL 3.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to answer merchant questions about stock levels, reorder alerts, sales orders, and customers through a read-only MCP interface, with OAuth 2.0, rate-limit handling, and a mock backend for testing.
    MIT